diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..f0fae18d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release +"on": + push: + branches: + - master + - next + - beta + - "*.x" +permissions: + contents: read # for checkout +jobs: + release: + 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 + name: release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + cache: npm + node-version: lts/* + - run: npm clean-install + - run: npm install --global corepack@latest + - run: corepack npm audit signatures + - run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_BOT_NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..83878b26 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,73 @@ +name: Test +"on": + push: + branches: + - master + - renovate/** + pull_request: + types: + - opened + - synchronize + +env: + FORCE_COLOR: 1 + NPM_CONFIG_COLOR: always + +jobs: + # verify against ranges defined as supported in engines.node + test_matrix: + strategy: + matrix: + node-version: + - 20.8.1 + - 20 + - 21 + os: + - ubuntu-latest + runs-on: "${{ matrix.os }}" + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: "Use Node.js ${{ matrix.node-version }}" + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "${{ matrix.node-version }}" + cache: npm + - run: npm clean-install + - run: npm install --global corepack@latest + - run: corepack npm audit signatures + - run: npm test + + # verify against the node version defined for development in the .nvmrc + test_dev: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - run: git config --global user.name github-actions + - run: git config --global user.email github-actions@github.com + - name: Use Node.js from .nvmrc + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version-file: .nvmrc + cache: npm + - run: npm clean-install + - run: npm install --global corepack@latest + - run: corepack npm audit signatures + - run: npm test + + # separate job to set as required in branch protection, + # as the build names above change each time Node versions change + test: + runs-on: ubuntu-latest + needs: + - test_dev + - test_matrix + if: always() + steps: + - name: All matrix versions passed + if: ${{ !(contains(needs.*.result, 'failure')) }} + run: exit 0 + - name: Some matrix version failed + if: ${{ contains(needs.*.result, 'failure') }} + run: exit 1 diff --git a/.gitignore b/.gitignore index fa6f8ca3..3a2dba9f 100644 --- a/.gitignore +++ b/.gitignore @@ -125,7 +125,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.gitignore.io/api/macos,windows,linux,node - -package-lock.json -yarn.lock +# End of https://www.gitignore.io/api/macos,windows,linux,node \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 43c97e71..00000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4b5fcd36..00000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: node_js - -node_js: - - 12 - - 10 - - 8.3 - -# Trigger a push build on master and greenkeeper branches + PRs build on every branches -# Avoid double build on PRs (See https://github.com/travis-ci/travis-ci/issues/1147) -branches: - only: - - master - - /^greenkeeper.*$/ - -# Retry install on fail to avoid failing a build on network/disk/external errors -install: - - travis_retry npm install - -script: - - npm run test - -after_success: - - npm run codecov - -jobs: - include: - - stage: release - node_js: lts/* - script: - - npm run semantic-release diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index acaaffdb..00000000 --- a/.yarnrc +++ /dev/null @@ -1 +0,0 @@ ---install.no-lockfile true diff --git a/README.md b/README.md index 06fdd2a1..b5ce46ff 100644 --- a/README.md +++ b/README.md @@ -3,22 +3,26 @@ [**semantic-release**](https://github.com/semantic-release/semantic-release) plugin to publish a [GitHub release](https://help.github.com/articles/about-releases) and comment on released Pull Requests/Issues. -[![Travis](https://img.shields.io/travis/semantic-release/github.svg)](https://travis-ci.org/semantic-release/github) -[![Codecov](https://img.shields.io/codecov/c/github/semantic-release/github.svg)](https://codecov.io/gh/semantic-release/github) -[![Greenkeeper badge](https://badges.greenkeeper.io/semantic-release/github.svg)](https://greenkeeper.io/) +[![Build Status](https://github.com/semantic-release/github/workflows/Test/badge.svg)](https://github.com/semantic-release/github/actions?query=workflow%3ATest+branch%3Amaster) [![npm latest version](https://img.shields.io/npm/v/@semantic-release/github/latest.svg)](https://www.npmjs.com/package/@semantic-release/github) [![npm next version](https://img.shields.io/npm/v/@semantic-release/github/next.svg)](https://www.npmjs.com/package/@semantic-release/github) +[![npm beta version](https://img.shields.io/npm/v/@semantic-release/github/beta.svg)](https://www.npmjs.com/package/@semantic-release/github) | Step | Description | -|--------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `verifyConditions` | Verify the presence and the validity of the authentication (set via [environment variables](#environment-variables)) and the [assets](#assets) option configuration. | | `publish` | Publish a [GitHub release](https://help.github.com/articles/about-releases), optionally uploading file assets. | +| `addChannel` | Update a [GitHub release](https://help.github.com/articles/about-releases)'s `pre-release` field. | | `success` | Add a comment to each [GitHub Issue](https://help.github.com/articles/about-issues) or [Pull Request](https://help.github.com/articles/about-pull-requests) resolved by the release and close issues previously open by the `fail` step. | | `fail` | Open or update a [GitHub Issue](https://help.github.com/articles/about-issues) with information about the errors that caused the release to fail. | ## Install +> [!TIP] +> You do not need to directly depend on this package if you are using `semantic-release`. +> `semantic-release` already depends on this package, and defining your own direct dependency can result in conflicts when you update `semantic-release`. + ```bash $ npm install @semantic-release/github -D ``` @@ -32,12 +36,15 @@ The plugin can be configured in the [**semantic-release** configuration file](ht "plugins": [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", - ["@semantic-release/github", { - "assets": [ - {"path": "dist/asset.min.css", "label": "CSS distribution"}, - {"path": "dist/asset.min.js", "label": "JS distribution"} - ] - }], + [ + "@semantic-release/github", + { + "assets": [ + { "path": "dist/asset.min.css", "label": "CSS distribution" }, + { "path": "dist/asset.min.js", "label": "JS distribution" } + ] + } + ] ] } ``` @@ -57,35 +64,53 @@ When creating the token, the **minimum required scopes** are: - [`repo`](https://github.com/settings/tokens/new?scopes=repo) for a private repository - [`public_repo`](https://github.com/settings/tokens/new?scopes=public_repo) for a public repository +_Note on GitHub Actions:_ You can use the default token which is provided in the secret _GITHUB_TOKEN_. However releases done with this token will NOT trigger release events to start other workflows. +If you have actions that trigger on newly created releases, please use a generated token for that and store it in your repository's secrets (any other name than GITHUB_TOKEN is fine). + +When using the _GITHUB_TOKEN_, the **minimum required permissions** are: + +- `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 + ### Environment variables -| Variable | Description | -| ------------------------------ | --------------------------------------------------------- | -| `GH_TOKEN` or `GITHUB_TOKEN` | **Required.** The token used to authenticate with GitHub. | -| `GH_URL` or `GITHUB_URL` | The GitHub Enterprise endpoint. | -| `GH_PREFIX` or `GITHUB_PREFIX` | The GitHub Enterprise API prefix. | +| Variable | Description | +| ------------------------------ | ------------------------------------------------------------------- | +| `GITHUB_TOKEN` or `GH_TOKEN` | **Required.** The token used to authenticate with GitHub. | +| `GITHUB_URL` or `GH_URL` | The GitHub server endpoint. | +| `GITHUB_PREFIX` or `GH_PREFIX` | The GitHub API prefix, relative to `GITHUB_URL`. | +| `GITHUB_API_URL` | The GitHub API endpoint. Note that this overwrites `GITHUB_PREFIX`. | ### Options -| Option | Description | Default | -|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| `githubUrl` | The GitHub Enterprise endpoint. | `GH_URL` or `GITHUB_URL` environment variable. | -| `githubApiPathPrefix` | The GitHub Enterprise API prefix. | `GH_PREFIX` or `GITHUB_PREFIX` environment variable. | -| `proxy` | The proxy to use to access the GitHub API. See [proxy](#proxy). | `HTTP_PROXY` environment variable. | -| `assets` | An array of files to upload to the release. See [assets](#assets). | - | -| `successComment` | The comment to add to each issue and pull request resolved by the release. Set to `false` to disable commenting on issues and pull requests. See [successComment](#successcomment). | `:tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitHub release]()` | -| `failComment` | The content of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. See [failComment](#failcomment). | Friendly message with links to **semantic-release** documentation and support, with the list of errors that caused the release to fail. | -| `failTitle` | The title of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. | `The automated release is failing 🚨` | -| `labels` | The [labels](https://help.github.com/articles/about-labels) to add to the issue created when a release fails. Set to `false` to not add any label. | `['semantic-release']` | -| `assignees` | The [assignees](https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users) to add to the issue created when a release fails. | - | -| `releasedLabels` | The [labels](https://help.github.com/articles/about-labels) to add to each issue and pull request resolved by the release. Set to `false` to not add any label. | `['released']` | +| Option | Description | Default | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `githubUrl` | The GitHub server endpoint. | `GH_URL` or `GITHUB_URL` environment variable. | +| `githubApiPathPrefix` | The GitHub API prefix, relative to `githubUrl`. | `GH_PREFIX` or `GITHUB_PREFIX` environment variable. | +| `githubApiUrl` | The GitHub API endpoint. Note that this overwrites `githubApiPathPrefix`. | `GITHUB_API_URL` environment variable. | +| `proxy` | The proxy to use to access the GitHub API. Set to `false` to disable usage of proxy. See [proxy](#proxy). | `HTTP_PROXY` environment variable. | +| `assets` | An array of files to upload to the release. See [assets](#assets). | - | +| `successComment` | The comment to add to each issue and pull request resolved by the release. Set to `false` to disable commenting on issues and pull requests. See [successComment](#successcomment). | `:tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitHub release]()` | +| `successCommentCondition` | Use this as condition, when to comment on issues or pull requests. See [successCommentCondition](#successCommentCondition) | - | +| `failComment` | The content of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. See [failComment](#failcomment). | Friendly message with links to **semantic-release** documentation and support, with the list of errors that caused the release to fail. | +| `failTitle` | The title of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. | `The automated release is failing 🚨` | +| `failCommentCondition` | Use this as condition, when to comment on or create an issues in case of failures. See [failCommentCondition](#failCommentCondition). | - | +| `labels` | The [labels](https://help.github.com/articles/about-labels) to add to the issue created when a release fails. Set to `false` to not add any label. | `['semantic-release']` | +| `assignees` | The [assignees](https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users) to add to the issue created when a release fails. | - | +| `releasedLabels` | The [labels](https://help.github.com/articles/about-labels) to add to each issue and pull request resolved by the release. Set to `false` to not add any label. See [releasedLabels](#releasedlabels). | `['released<%= nextRelease.channel ? \` on @\${nextRelease.channel}\` : "" %>']- | +| `addReleases` | Will add release links to the GitHub Release. Can be `false`, `"bottom"` or `"top"`. See [addReleases](#addReleases). | `false` | +| `draftRelease` | A boolean indicating if a GitHub Draft Release should be created instead of publishing an actual GitHub Release. | `false` | +| `releaseNameTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's name | `<%= nextverison.name %>` | +| `releaseBodyTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's body | `<%= nextverison.notes %>` | +| `discussionCategoryName` | The category name in which to create a linked discussion to the release. Set to `false` to disable creating discussion for a release. | `false` | #### proxy -Can be a the proxy URL or and `Object` with the following properties: +Can be `false`, a proxy URL or an `Object` with the following properties: | Property | Description | Default | -|---------------|----------------------------------------------------------------|--------------------------------------| +| ------------- | -------------------------------------------------------------- | ------------------------------------ | | `host` | **Required.** Proxy host to connect to. | - | | `port` | **Required.** Proxy port to connect to. | File name extracted from the `path`. | | `secureProxy` | If `true`, then use TLS to connect to the proxy. | `false` | @@ -118,7 +143,7 @@ If a directory is configured, all the files under this directory and its childre The `name` and `label` for each assets are generated with [Lodash template](https://lodash.com/docs#template). The following variables are available: | Parameter | Description | -|---------------|-------------------------------------------------------------------------------------| +| ------------- | ----------------------------------------------------------------------------------- | | `branch` | The branch from which the release is done. | | `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. | | `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. | @@ -133,9 +158,7 @@ The `name` and `label` for each assets are generated with [Lodash template](http `[['dist', '!**/*.css']]`: include all the files in the `dist` directory and its sub-directories excluding the `css` files. -`[{path: 'dist/MyLibrary.js', label: 'MyLibrary JS distribution'}, {path: 'dist/MyLibrary.css', label: 'MyLibrary CSS -distribution'}]`: include the `dist/MyLibrary.js` and `dist/MyLibrary.css` files, and label them `MyLibrary JS -distribution` and `MyLibrary CSS distribution` in the GitHub release. +`[{path: 'dist/MyLibrary.js', label: 'MyLibrary JS distribution'}, {path: 'dist/MyLibrary.css', label: 'MyLibrary CSS distribution'}]`: include the `dist/MyLibrary.js` and `dist/MyLibrary.css` files, and label them `MyLibrary JS distribution` and `MyLibrary CSS distribution` in the GitHub release. `[['dist/**/*.{js,css}', '!**/*.min.*'], {path: 'build/MyLibrary.zip', label: 'MyLibrary'}]`: include all the `js` and `css` files in the `dist` directory and its sub-directories excluding the minified version, plus the @@ -149,34 +172,108 @@ distribution` and `MyLibrary CSS distribution` in the GitHub release. The message for the issue comments is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available: -| Parameter | Description | -|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `branch` | The branch from which the release is done. | -| `lastRelease` | `Object` with `version`, `gitTag` and `gitHead` of the last release. | -| `nextRelease` | `Object` with `version`, `gitTag`, `gitHead` and `notes` of the release being done. | -| `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. | -| `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. | -| `issue` | A [GitHub API pull request object](https://developer.github.com/v3/search/#search-issues) for pull requests related to a commit, or an `Object` with the `number` property for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) | +| Parameter | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. | +| `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. | +| `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. | +| `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. | +| `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. | +| `issue` | A [GitHub API pull request object](https://developer.github.com/v3/search/#search-issues) for pull requests related to a commit, or [GitHub API issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue) for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) | -##### successComment examples +##### successComment example The `successComment` `This ${issue.pull_request ? 'pull request' : 'issue'} is included in version ${nextRelease.version}` will generate the comment: > This pull request is included in version 1.0.0 +#### successCommentCondition + +A [Lodash template](https://lodash.com/docs#template) string that should evaluate to a truthy or falsy variable. The following variables are available: + +| Parameter | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. | +| `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. | +| `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. | +| `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. | +| `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. | +| `issue` | A [GitHub API Pull Request object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for pull requests related to a commit | + +##### successCommentCondition example + +- do not create any comments at all: set to `false` or templating: `"<% return false; %>"` +- to only comment on issues: `"<% return !issue.pull_request; %>"` +- to only comment on pull requests: `"<% return issue.pull_request; %>"` +- to avoid comment on PRs or issues created by Bots: `"<% return issue.user.type !== 'Bot'; %>"` +- you can use labels to filter issues: `"<% return issue.labels?.some((label) => { return label.name === ('semantic-release-relevant'); }); %>"` + +> check the [GitHub API issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue) for properties which can be used for the filter + #### failComment The message for the issue content is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available: | Parameter | Description | -|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `branch` | The branch from which the release had failed. | | `errors` | An `Array` of [SemanticReleaseError](https://github.com/semantic-release/error). Each error has the `message`, `code`, `pluginName` and `details` properties.
`pluginName` contains the package name of the plugin that threw the error.
`details` contains a information about the error formatted in markdown. | -##### failComment examples +##### failComment example -The `failComment` `This release from branch ${branch} had failed due to the following errors:\n- ${errors.map(err => err.message).join('\\n- ')}` will generate the comment: +The `failComment` `This release from branch ${branch.name} had failed due to the following errors:\n- ${errors.map(err => err.message).join('\\n- ')}` will generate the comment: > This release from branch master had failed due to the following errors: +> > - Error message 1 > - Error message 2 + +#### failCommentCondition + +A [Lodash template](https://lodash.com/docs#template) string that should evaluate to a truthy or falsy variable. The following variables are available: + +| Parameter | Description | +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. | +| `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. | +| `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. | +| `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. | +| `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. | +| `issue` | A [GitHub API pull request object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for pull requests related to a commit, or [GitHub API issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue) for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) | + +##### failCommentCondition example + +- do not create any comments at all: set to `false` or templating: `"<% return false; %>"` +- to only comment on main branch: `"<% return branch.name === 'main' %>"` +- you can use labels to filter issues, i.e. to not comment if the issue is labeled with `wip`: `"<% return !issue.labels?.includes('wip') %>"` + +> check the [GitHub API Pull Request Object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for properties which can be used for the filter + +#### releasedLabels + +Each label name is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available: + +| Parameter | Description | +| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. | +| `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. | +| `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. | +| `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. | +| `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. | +| `issue` | A [GitHub API pull request object](https://developer.github.com/v3/search/#search-issues) for pull requests related to a commit, or [GitHub API issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue) for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) | + +##### releasedLabels example + +The `releasedLabels` ``['released<%= nextRelease.channel ? ` on @\${nextRelease.channel}` : "" %> from <%= branch.name %>']`` will generate the label: + +> released on @next from branch next + +#### addReleases + +Add links to other releases to the GitHub release body. + +Valid values for this option are `false`, `"top"` or `"bottom"`. + +##### addReleases example + +See [The introducing PR](https://github.com/semantic-release/github/pull/282) for an example on how it will look. diff --git a/index.js b/index.js index a0639295..2d1aa8b8 100644 --- a/index.js +++ b/index.js @@ -1,57 +1,105 @@ /* eslint require-atomic-updates: off */ -const {defaultTo, castArray} = require('lodash'); -const verifyGitHub = require('./lib/verify'); -const publishGitHub = require('./lib/publish'); -const successGitHub = require('./lib/success'); -const failGitHub = require('./lib/fail'); +import { defaultTo, castArray } from "lodash-es"; + +import verifyGitHub from "./lib/verify.js"; +import addChannelGitHub from "./lib/add-channel.js"; +import publishGitHub from "./lib/publish.js"; +import successGitHub from "./lib/success.js"; +import failGitHub from "./lib/fail.js"; +import { SemanticReleaseOctokit } from "./lib/octokit.js"; let verified; -async function verifyConditions(pluginConfig, context) { - const {options} = context; - // If the GitHub publish plugin is used and has `assets`, `successComment`, `failComment`, `failTitle`, `labels` or `assignees` configured, validate it now in order to prevent any release if the configuration is wrong +export async function verifyConditions( + pluginConfig, + context, + { Octokit = SemanticReleaseOctokit } = {}, +) { + const { options } = context; + // If the GitHub publish plugin is used and has `assets`, `successComment`, `failComment`, `failTitle`, `labels`, `discussionCategoryName` or `assignees` configured, validate it now in order to prevent any release if the configuration is wrong if (options.publish) { const publishPlugin = - castArray(options.publish).find(config => config.path && config.path === '@semantic-release/github') || {}; + castArray(options.publish).find( + (config) => config.path && config.path === "@semantic-release/github", + ) || {}; pluginConfig.assets = defaultTo(pluginConfig.assets, publishPlugin.assets); - pluginConfig.successComment = defaultTo(pluginConfig.successComment, publishPlugin.successComment); - pluginConfig.failComment = defaultTo(pluginConfig.failComment, publishPlugin.failComment); - pluginConfig.failTitle = defaultTo(pluginConfig.failTitle, publishPlugin.failTitle); + pluginConfig.successComment = defaultTo( + pluginConfig.successComment, + publishPlugin.successComment, + ); + pluginConfig.failComment = defaultTo( + pluginConfig.failComment, + publishPlugin.failComment, + ); + pluginConfig.failTitle = defaultTo( + pluginConfig.failTitle, + publishPlugin.failTitle, + ); pluginConfig.labels = defaultTo(pluginConfig.labels, publishPlugin.labels); - pluginConfig.assignees = defaultTo(pluginConfig.assignees, publishPlugin.assignees); + pluginConfig.assignees = defaultTo( + pluginConfig.assignees, + publishPlugin.assignees, + ); + pluginConfig.discussionCategoryName = defaultTo( + pluginConfig.discussionCategoryName, + publishPlugin.discussionCategoryName, + ); } - await verifyGitHub(pluginConfig, context); + await verifyGitHub(pluginConfig, context, { Octokit }); verified = true; } -async function publish(pluginConfig, context) { +export async function publish( + pluginConfig, + context, + { Octokit = SemanticReleaseOctokit } = {}, +) { if (!verified) { - await verifyGitHub(pluginConfig, context); + await verifyGitHub(pluginConfig, context, { Octokit }); verified = true; } - return publishGitHub(pluginConfig, context); + return publishGitHub(pluginConfig, context, { Octokit }); } -async function success(pluginConfig, context) { +export async function addChannel( + pluginConfig, + context, + { Octokit = SemanticReleaseOctokit } = {}, +) { if (!verified) { - await verifyGitHub(pluginConfig, context); + await verifyGitHub(pluginConfig, context, { Octokit }); verified = true; } - await successGitHub(pluginConfig, context); + return addChannelGitHub(pluginConfig, context, { Octokit }); } -async function fail(pluginConfig, context) { +export async function success( + pluginConfig, + context, + { Octokit = SemanticReleaseOctokit } = {}, +) { if (!verified) { - await verifyGitHub(pluginConfig, context); + await verifyGitHub(pluginConfig, context, { Octokit }); verified = true; } - await failGitHub(pluginConfig, context); + await successGitHub(pluginConfig, context, { Octokit }); } -module.exports = {verifyConditions, publish, success, fail}; +export async function fail( + pluginConfig, + context, + { Octokit = SemanticReleaseOctokit } = {}, +) { + if (!verified) { + await verifyGitHub(pluginConfig, context, { Octokit }); + verified = true; + } + + await failGitHub(pluginConfig, context, { Octokit }); +} diff --git a/lib/add-channel.js b/lib/add-channel.js new file mode 100644 index 00000000..60698539 --- /dev/null +++ b/lib/add-channel.js @@ -0,0 +1,80 @@ +import debugFactory from "debug"; + +import { RELEASE_NAME } from "./definitions/constants.js"; +import parseGithubUrl from "./parse-github-url.js"; +import resolveConfig from "./resolve-config.js"; +import isPrerelease from "./is-prerelease.js"; +import { toOctokitOptions } from "./octokit.js"; + +const debug = debugFactory("semantic-release:github"); + +export default async function addChannel(pluginConfig, context, { Octokit }) { + const { + options: { repositoryUrl }, + branch, + nextRelease: { name, gitTag, notes }, + logger, + } = context; + const { githubToken, githubUrl, githubApiPathPrefix, githubApiUrl, proxy } = + resolveConfig(pluginConfig, context); + const { owner, repo } = parseGithubUrl(repositoryUrl); + const octokit = new Octokit( + toOctokitOptions({ + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + }), + ); + let releaseId; + + const release = { + owner, + repo, + name, + prerelease: isPrerelease(branch), + tag_name: gitTag, + }; + + debug("release object: %O", release); + + try { + ({ + data: { id: releaseId }, + } = await octokit.request("GET /repos/{owner}/{repo}/releases/tags/{tag}", { + owner, + repo, + tag: gitTag, + })); + } catch (error) { + if (error.status === 404) { + logger.log("There is no release for tag %s, creating a new one", gitTag); + + const { + data: { html_url: url }, + } = await octokit.request("POST /repos/{owner}/{repo}/releases", { + ...release, + body: notes, + }); + + logger.log("Published GitHub release: %s", url); + return { url, name: RELEASE_NAME }; + } + + throw error; + } + + debug("release release_id: %o", releaseId); + + const { + data: { html_url: url }, + } = await octokit.request( + "PATCH /repos/{owner}/{repo}/releases/{release_id}", + { ...release, release_id: releaseId }, + ); + + logger.log("Updated GitHub release: %s", url); + + return { url, name: RELEASE_NAME }; +} diff --git a/lib/definitions/constants.js b/lib/definitions/constants.js new file mode 100644 index 00000000..c0400947 --- /dev/null +++ b/lib/definitions/constants.js @@ -0,0 +1,5 @@ +export const ISSUE_ID = ""; + +export const RELEASE_NAME = "GitHub release"; + +export const RELEASE_FAIL_LABEL = "semantic-release"; diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index 1a5a319b..74fa1ce2 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -1,108 +1,249 @@ -const {inspect} = require('util'); -const {isString} = require('lodash'); -const pkg = require('../../package.json'); - -const [homepage] = pkg.homepage.split('#'); -const stringify = obj => (isString(obj) ? obj : inspect(obj, {breakLength: Infinity, depth: 2, maxArrayLength: 5})); -const linkify = file => `${homepage}/blob/master/${file}`; - -module.exports = { - EINVALIDASSETS: ({assets}) => ({ - message: 'Invalid `assets` option.', +import { inspect } from "node:util"; +import { createRequire } from "node:module"; +const require = createRequire(import.meta.url); + +import { isString } from "lodash-es"; + +const pkg = require("../../package.json"); +const HOMEPAGE = pkg.homepage; + +const stringify = (object) => + isString(object) + ? object + : inspect(object, { + breakLength: Number.POSITIVE_INFINITY, + depth: 2, + maxArrayLength: 5, + }); +const linkify = (file) => `${HOMEPAGE}/blob/master/${file}`; + +export function EINVALIDASSETS({ assets }) { + return { + message: "Invalid `assets` option.", details: `The [assets option](${linkify( - 'README.md#assets' + "README.md#assets", )}) must be an \`Array\` of \`Strings\` or \`Objects\` with a \`path\` property. Your configuration for the \`assets\` option is \`${stringify(assets)}\`.`, - }), - EINVALIDSUCCESSCOMMENT: ({successComment}) => ({ - message: 'Invalid `successComment` option.', + }; +} + +export function EINVALIDSUCCESSCOMMENT({ successComment }) { + return { + message: "Invalid `successComment` option.", details: `The [successComment option](${linkify( - 'README.md#successcomment' + "README.md#successcomment", + )}) if defined, must be a non empty \`String\`. + +Your configuration for the \`successComment\` option is \`${stringify( + successComment, + )}\`.`, + }; +} + +export function EINVALIDFAILTITLE({ failTitle }) { + return { + message: "Invalid `failTitle` option.", + details: `The [failTitle option](${linkify( + "README.md#failtitle", )}) if defined, must be a non empty \`String\`. -Your configuration for the \`successComment\` option is \`${stringify(successComment)}\`.`, - }), - EINVALIDFAILTITLE: ({failTitle}) => ({ - message: 'Invalid `failTitle` option.', - details: `The [failTitle option](${linkify('README.md#failtitle')}) if defined, must be a non empty \`String\`. - -Your configuration for the \`failTitle\` option is \`${stringify(failTitle)}\`.`, - }), - EINVALIDFAILCOMMENT: ({failComment}) => ({ - message: 'Invalid `failComment` option.', - details: `The [failComment option](${linkify('README.md#failcomment')}) if defined, must be a non empty \`String\`. - -Your configuration for the \`failComment\` option is \`${stringify(failComment)}\`.`, - }), - EINVALIDLABELS: ({labels}) => ({ - message: 'Invalid `labels` option.', +Your configuration for the \`failTitle\` option is \`${stringify( + failTitle, + )}\`.`, + }; +} + +export function EINVALIDFAILCOMMENT({ failComment }) { + return { + message: "Invalid `failComment` option.", + details: `The [failComment option](${linkify( + "README.md#failcomment", + )}) if defined, must be a non empty \`String\`. + +Your configuration for the \`failComment\` option is \`${stringify( + failComment, + )}\`.`, + }; +} + +export function EINVALIDLABELS({ labels }) { + return { + message: "Invalid `labels` option.", details: `The [labels option](${linkify( - 'README.md#options' + "README.md#options", )}) if defined, must be an \`Array\` of non empty \`String\`. Your configuration for the \`labels\` option is \`${stringify(labels)}\`.`, - }), - EINVALIDASSIGNEES: ({assignees}) => ({ - message: 'Invalid `assignees` option.', - details: `The [assignees option](${linkify('README.md#options')}) must be an \`Array\` of non empty \`Strings\`. - -Your configuration for the \`assignees\` option is \`${stringify(assignees)}\`.`, - }), - EINVALIDRELEASEDLABELS: ({releasedLabels}) => ({ - message: 'Invalid `releasedLabels` option.', + }; +} + +export function EINVALIDASSIGNEES({ assignees }) { + return { + message: "Invalid `assignees` option.", + details: `The [assignees option](${linkify( + "README.md#options", + )}) must be an \`Array\` of non empty \`Strings\`. + +Your configuration for the \`assignees\` option is \`${stringify( + assignees, + )}\`.`, + }; +} + +export function EINVALIDRELEASEDLABELS({ releasedLabels }) { + return { + message: "Invalid `releasedLabels` option.", details: `The [releasedLabels option](${linkify( - 'README.md#options' + "README.md#options", )}) if defined, must be an \`Array\` of non empty \`String\`. -Your configuration for the \`releasedLabels\` option is \`${stringify(releasedLabels)}\`.`, - }), - EINVALIDGITHUBURL: () => ({ - message: 'The git repository URL is not a valid GitHub URL.', +Your configuration for the \`releasedLabels\` option is \`${stringify( + releasedLabels, + )}\`.`, + }; +} + +export function EINVALIDADDRELEASES({ addReleases }) { + return { + message: "Invalid `addReleases` option.", + details: `The [addReleases option](${linkify( + "README.md#options", + )}) if defined, must be one of \`false|top|bottom\`. + +Your configuration for the \`addReleases\` option is \`${stringify( + addReleases, + )}\`.`, + }; +} + +export function EINVALIDDRAFTRELEASE({ draftRelease }) { + return { + message: "Invalid `draftRelease` option.", + details: `The [draftRelease option](${linkify( + "README.md#options", + )}) if defined, must be a \`Boolean\`. + +Your configuration for the \`draftRelease\` option is \`${stringify( + draftRelease, + )}\`.`, + }; +} + +export function EINVALIDGITHUBURL() { + return { + message: "The git repository URL is not a valid GitHub URL.", details: `The **semantic-release** \`repositoryUrl\` option must a valid GitHub URL with the format \`//.git\`. By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment.`, - }), - EINVALIDPROXY: ({proxy}) => ({ - message: 'Invalid `proxy` option.', + }; +} + +export function EMISMATCHGITHUBURL({ repositoryUrl, clone_url }) { + return { + message: "The git repository URL mismatches the GitHub URL.", + details: `The **semantic-release** \`repositoryUrl\` option must have the same repository name and owner as the GitHub repo. + +Your configuration for the \`repositoryUrl\` option is \`${stringify(repositoryUrl)}\` and the \`clone_url\` of your GitHub repo is \`${stringify(clone_url)}\`. + +By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. + +Note: If you have recently changed your GitHub repository name or owner, update the value in **semantic-release** \`repositoryUrl\` option and the \`repository\` property of your \`package.json\` respectively to match the new GitHub URL.`, + }; +} + +export function EINVALIDPROXY({ proxy }) { + return { + message: "Invalid `proxy` option.", details: `The [proxy option](${linkify( - 'README.md#proxy' + "README.md#proxy", )}) must be a \`String\` or an \`Objects\` with a \`host\` and a \`port\` property. Your configuration for the \`proxy\` option is \`${stringify(proxy)}\`.`, - }), - EMISSINGREPO: ({owner, repo}) => ({ + }; +} + +export function EMISSINGREPO({ owner, repo }) { + return { message: `The repository ${owner}/${repo} doesn't exist.`, details: `The **semantic-release** \`repositoryUrl\` option must refer to your GitHub repository. The repository must be accessible with the [GitHub API](https://developer.github.com/v3). By default the \`repositoryUrl\` option is retrieved from the \`repository\` property of your \`package.json\` or the [git origin url](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) of the repository cloned by your CI environment. If you are using [GitHub Enterprise](https://enterprise.github.com) please make sure to configure the \`githubUrl\` and \`githubApiPathPrefix\` [options](${linkify( - 'README.md#options' + "README.md#options", )}).`, - }), - EGHNOPERMISSION: ({owner, repo}) => ({ + }; +} + +export function EGHNOPERMISSION({ owner, repo }) { + return { message: `The GitHub token doesn't allow to push on the repository ${owner}/${repo}.`, details: `The user associated with the [GitHub token](${linkify( - 'README.md#github-authentication' + "README.md#github-authentication", )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must allows to push to the repository ${owner}/${repo}. -Please make sure the GitHub user associated with the token is an [owner](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#owner-access-on-a-repository-owned-by-a-user-account) or a [collaborator](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#collaborator-access-on-a-repository-owned-by-a-user-account) if the reposotory belong to a user account or has [write permissions](https://help.github.com/articles/managing-team-access-to-an-organization-repository) if the repository [belongs to an organization](https://help.github.com/articles/repository-permission-levels-for-an-organization).`, - }), - EINVALIDGHTOKEN: ({owner, repo}) => ({ - message: 'Invalid GitHub token.', +Please make sure the GitHub user associated with the token is an [owner](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#owner-access-on-a-repository-owned-by-a-user-account) or a [collaborator](https://help.github.com/articles/permission-levels-for-a-user-account-repository/#collaborator-access-on-a-repository-owned-by-a-user-account) if the repository belong to a user account or has [write permissions](https://help.github.com/articles/managing-team-access-to-an-organization-repository) if the repository [belongs to an organization](https://help.github.com/articles/repository-permission-levels-for-an-organization).`, + }; +} + +export function EINVALIDGHTOKEN({ owner, repo }) { + return { + message: "Invalid GitHub token.", details: `The [GitHub token](${linkify( - 'README.md#github-authentication' + "README.md#github-authentication", )}) configured in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable must be a valid [personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) allowing to push to the repository ${owner}/${repo}. Please make sure to set the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable in your CI with the exact value of the GitHub personal token.`, - }), - ENOGHTOKEN: ({owner, repo}) => ({ - message: 'No GitHub token specified.', + }; +} + +export function ENOGHTOKEN({ owner, repo }) { + return { + message: "No GitHub token specified.", details: `A [GitHub personal token](${linkify( - 'README.md#github-authentication' + "README.md#github-authentication", )}) must be created and set in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. Please make sure to create a [GitHub personal token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line) and to set it in the \`GH_TOKEN\` or \`GITHUB_TOKEN\` environment variable on your CI environment. The token must allow to push to the repository ${owner}/${repo}.`, - }), -}; + }; +} + +export function EINVALIDRELEASEBODYTEMPLATE({ releaseBodyTemplate }) { + return { + message: "Invalid `releaseBodyTemplate` option.", + details: `The [releaseBodyTemplate option](${linkify( + "README.md#releaseBodyTemplate", + )}) must be a non empty \`String\`. + +Your configuration for the \`releaseBodyTemplate\` option is \`${stringify( + releaseBodyTemplate, + )}\`.`, + }; +} + +export function EINVALIDRELEASENAMETEMPLATE({ releaseNameTemplate }) { + return { + message: "Invalid `releaseNameTemplate` option.", + details: `The [releaseNameTemplate option](${linkify( + "README.md#releaseNameTemplate", + )}) must be a non empty \`String\`. + +Your configuration for the \`releaseNameTemplate\` option is \`${stringify( + releaseNameTemplate, + )}\`.`, + }; +} + +export function EINVALIDDISCUSSIONCATEGORYNAME({ discussionCategoryName }) { + return { + message: "Invalid `discussionCategoryName` option.", + details: `The [discussionCategoryName option](${linkify( + "README.md#discussionCategoryName", + )}) if defined, must be a non empty \`String\`. + +Your configuration for the \`discussionCategoryName\` option is \`${stringify( + discussionCategoryName, + )}\`.`, + }; +} diff --git a/lib/definitions/rate-limit.js b/lib/definitions/rate-limit.js deleted file mode 100644 index cc666b71..00000000 --- a/lib/definitions/rate-limit.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Default exponential backoff configuration for retries. - */ -const RETRY_CONF = {retries: 3, factor: 2, minTimeout: 1000}; - -/** - * Rate limit per API endpoints. - * - * See {@link https://developer.github.com/v3/search/#rate-limit|Search API rate limit}. - * See {@link https://developer.github.com/v3/#rate-limiting|Rate limiting}. - */ -const RATE_LIMITS = { - search: ((60 * 1000) / 30) * 1.1, // 30 calls per minutes => 1 call every 2s + 10% safety margin - core: { - read: ((60 * 60 * 1000) / 5000) * 1.1, // 5000 calls per hour => 1 call per 720ms + 10% safety margin - write: 3000, // 1 call every 3 seconds - }, -}; - -/** - * Global rate limit to prevent abuse. - * - * See {@link https://developer.github.com/v3/guides/best-practices-for-integrators/#dealing-with-abuse-rate-limits|Dealing with abuse rate limits} - */ -const GLOBAL_RATE_LIMIT = 1000; - -module.exports = {RETRY_CONF, RATE_LIMITS, GLOBAL_RATE_LIMIT}; diff --git a/lib/definitions/retry.js b/lib/definitions/retry.js new file mode 100644 index 00000000..9b5021c3 --- /dev/null +++ b/lib/definitions/retry.js @@ -0,0 +1,9 @@ +/** + * Default retry config for octokit retry plugin + */ +export const RETRY_CONF = { + // By default, Octokit does not retry on 404s. + // But we want to retry on 404s to account for replication lag. + doNotRetry: [400, 401, 403, 422], + retries: 3, +}; diff --git a/lib/definitions/sr-issue-id.js b/lib/definitions/sr-issue-id.js deleted file mode 100644 index f53880a5..00000000 --- a/lib/definitions/sr-issue-id.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = ''; diff --git a/lib/definitions/throttle.js b/lib/definitions/throttle.js new file mode 100644 index 00000000..e524aa7b --- /dev/null +++ b/lib/definitions/throttle.js @@ -0,0 +1,5 @@ +/** + * Default configuration for throttle. + * @see https://github.com/octokit/plugin-throttling.js#options + */ +export const THROTTLE_CONF = {}; diff --git a/lib/fail.js b/lib/fail.js index 94d74919..b5b43a3f 100644 --- a/lib/fail.js +++ b/lib/fail.js @@ -1,47 +1,104 @@ -const {template} = require('lodash'); -const debug = require('debug')('semantic-release:github'); -const parseGithubUrl = require('./parse-github-url'); -const ISSUE_ID = require('./definitions/sr-issue-id'); -const resolveConfig = require('./resolve-config'); -const getClient = require('./get-client'); -const findSRIssues = require('./find-sr-issues'); -const getFailComment = require('./get-fail-comment'); +import { template } from "lodash-es"; +import debugFactory from "debug"; -module.exports = async (pluginConfig, context) => { +import parseGithubUrl from "./parse-github-url.js"; +import { ISSUE_ID, RELEASE_FAIL_LABEL } from "./definitions/constants.js"; +import resolveConfig from "./resolve-config.js"; +import { toOctokitOptions } from "./octokit.js"; +import findSRIssues from "./find-sr-issues.js"; +import getFailComment from "./get-fail-comment.js"; + +const debug = debugFactory("semantic-release:github"); + +export default async function fail(pluginConfig, context, { Octokit }) { const { - options: {branch, repositoryUrl}, + options: { repositoryUrl }, + branch, errors, logger, } = context; - const {githubToken, githubUrl, githubApiPathPrefix, proxy, failComment, failTitle, labels, assignees} = resolveConfig( - pluginConfig, - context - ); + const { + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + failTitle, + failComment, + failCommentCondition, + labels, + assignees, + } = resolveConfig(pluginConfig, context); if (failComment === false || failTitle === false) { - logger.log('Skip issue creation.'); + logger.log("Skip issue creation."); + logger.warn( + `DEPRECATION: 'false' for 'failComment' or 'failTitle' is deprecated and will be removed in a future major version. Use 'failCommentCondition' instead.`, + ); + } else if (failCommentCondition === false) { + logger.log("Skip issue creation."); } else { - const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy}); + const octokit = new Octokit( + toOctokitOptions({ + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + }), + ); // In case the repo changed name, get the new `repo`/`owner` as the search API will not follow redirects - const [owner, repo] = (await github.repos.get(parseGithubUrl(repositoryUrl))).data.full_name.split('/'); - const body = failComment ? template(failComment)({branch, errors}) : getFailComment(branch, errors); - const [srIssue] = await findSRIssues(github, failTitle, owner, repo); + const { data: repoData } = await octokit.request( + "GET /repos/{owner}/{repo}", + parseGithubUrl(repositoryUrl), + ); + const [owner, repo] = repoData.full_name.split("/"); + const body = failComment + ? template(failComment)({ branch, errors }) + : getFailComment(branch, errors); + const [srIssue] = await findSRIssues( + octokit, + logger, + failTitle, + labels, + owner, + repo, + ); + + const canCommentOnOrCreateIssue = failCommentCondition + ? template(failCommentCondition)({ ...context, issue: srIssue }) + : true; + + if (!canCommentOnOrCreateIssue) { + logger.log("Skip commenting on or creating an issue."); + return; + } if (srIssue) { - logger.log('Found existing semantic-release issue #%d.', srIssue.number); - const comment = {owner, repo, issue_number: srIssue.number, body}; - debug('create comment: %O', comment); + logger.log("Found existing semantic-release issue #%d.", srIssue.number); + const comment = { owner, repo, issue_number: srIssue.number, body }; + debug("create comment: %O", comment); const { - data: {html_url: url}, - } = await github.issues.createComment(comment); - logger.log('Added comment to issue #%d: %s.', srIssue.number, url); + data: { html_url: url }, + } = await octokit.request( + "POST /repos/{owner}/{repo}/issues/{issue_number}/comments", + comment, + ); + logger.log("Added comment to issue #%d: %s.", srIssue.number, url); } else { - const newIssue = {owner, repo, title: failTitle, body: `${body}\n\n${ISSUE_ID}`, labels: labels || [], assignees}; - debug('create issue: %O', newIssue); + const newIssue = { + owner, + repo, + title: failTitle, + body: `${body}\n\n${ISSUE_ID}`, + labels: (labels || []).concat([RELEASE_FAIL_LABEL]), + assignees, + }; + debug("create issue: %O", newIssue); const { - data: {html_url: url, number}, - } = await github.issues.create(newIssue); - logger.log('Created issue #%d: %s.', number, url); + data: { html_url: url, number }, + } = await octokit.request("POST /repos/{owner}/{repo}/issues", newIssue); + logger.log("Created issue #%d: %s.", number, url); } } -}; +} diff --git a/lib/find-sr-issues.js b/lib/find-sr-issues.js index d99c14a4..47af4104 100644 --- a/lib/find-sr-issues.js +++ b/lib/find-sr-issues.js @@ -1,11 +1,63 @@ -const ISSUE_ID = require('./definitions/sr-issue-id'); +import { uniqBy } from "lodash-es"; +import { ISSUE_ID, RELEASE_FAIL_LABEL } from "./definitions/constants.js"; + +export default async (octokit, logger, title, labels, owner, repo) => { + let issues = []; -module.exports = async (github, title, owner, repo) => { const { - data: {items: issues}, - } = await github.search.issuesAndPullRequests({ - q: `in:title+repo:${owner}/${repo}+type:issue+state:open+${title}`, + repository: { + issues: { nodes: issueNodes }, + }, + } = await octokit.graphql(loadGetSRIssuesQuery, { + owner, + repo, + filter: { + labels: (labels || []).concat([RELEASE_FAIL_LABEL]), + }, }); - return issues.filter(issue => issue.body && issue.body.includes(ISSUE_ID)); + issues.push(...issueNodes); + + /** + * BACKWARD COMPATIBILITY: Fallback to the search API if the issue was not found in the GraphQL response. + * This fallback will be removed in a future release + */ + if (issueNodes.length === 0) { + try { + const { + data: { items: backwardIssues }, + } = await octokit.request("GET /search/issues", { + q: `in:title+repo:${owner}/${repo}+type:issue+state:open+${title}`, + }); + issues.push(...backwardIssues); + } catch (error) { + logger.log( + "An error occured fetching issue via fallback (with GH SearchAPI)", + ); + } + } + + const uniqueSRIssues = uniqBy( + issues.filter((issue) => issue.body && issue.body.includes(ISSUE_ID)), + "number", + ); + + return uniqueSRIssues; }; + +/** + * GraphQL Query to et the semantic-release issues for a repository. + */ +const loadGetSRIssuesQuery = `#graphql + query getSRIssues($owner: String!, $repo: String!, $filter: IssueFilters) { + repository(owner: $owner, name: $repo) { + issues(first: 100, states: OPEN, filterBy: $filter) { + nodes { + number + title + body + } + } + } + } +`; diff --git a/lib/get-client.js b/lib/get-client.js deleted file mode 100644 index cab547f1..00000000 --- a/lib/get-client.js +++ /dev/null @@ -1,64 +0,0 @@ -const {URL} = require('url'); -const {memoize, get} = require('lodash'); -const Octokit = require('@octokit/rest'); -const pRetry = require('p-retry'); -const Bottleneck = require('bottleneck'); -const urljoin = require('url-join'); -const HttpProxyAgent = require('http-proxy-agent'); -const HttpsProxyAgent = require('https-proxy-agent'); - -const {RETRY_CONF, RATE_LIMITS, GLOBAL_RATE_LIMIT} = require('./definitions/rate-limit'); - -/** - * Http error status for which to not retry. - */ -const SKIP_RETRY_CODES = [400, 401, 403]; - -/** - * Create or retrieve the throttler function for a given rate limit group. - * - * @param {Array} rate The rate limit group. - * @param {String} limit The rate limits per API endpoints. - * @param {Bottleneck} globalThrottler The global throttler. - * - * @return {Bottleneck} The throller function for the given rate limit group. - */ -const getThrottler = memoize((rate, globalThrottler) => - new Bottleneck({minTime: get(RATE_LIMITS, rate)}).chain(globalThrottler) -); - -module.exports = ({githubToken, githubUrl, githubApiPathPrefix, proxy}) => { - const baseUrl = githubUrl && urljoin(githubUrl, githubApiPathPrefix); - const globalThrottler = new Bottleneck({minTime: GLOBAL_RATE_LIMIT}); - const github = new Octokit({ - auth: `token ${githubToken}`, - baseUrl, - request: { - agent: proxy - ? baseUrl && new URL(baseUrl).protocol.replace(':', '') === 'http' - ? new HttpProxyAgent(proxy) - : new HttpsProxyAgent(proxy) - : undefined, - }, - }); - - github.hook.wrap('request', (request, options) => { - const access = options.method === 'GET' ? 'read' : 'write'; - const rateCategory = options.url.startsWith('/search') ? 'search' : 'core'; - const limitKey = [rateCategory, RATE_LIMITS[rateCategory][access] && access].filter(Boolean).join('.'); - - return pRetry(async () => { - try { - return await getThrottler(limitKey, globalThrottler).wrap(request)(options); - } catch (error) { - if (SKIP_RETRY_CODES.includes(error.status)) { - throw new pRetry.AbortError(error); - } - - throw error; - } - }, RETRY_CONF); - }); - - return github; -}; diff --git a/lib/get-error.js b/lib/get-error.js index 56a09c0d..639c48f5 100644 --- a/lib/get-error.js +++ b/lib/get-error.js @@ -1,7 +1,8 @@ -const SemanticReleaseError = require('@semantic-release/error'); -const ERROR_DEFINITIONS = require('./definitions/errors'); +import SemanticReleaseError from "@semantic-release/error"; -module.exports = (code, ctx = {}) => { - const {message, details} = ERROR_DEFINITIONS[code](ctx); +import * as ERROR_DEFINITIONS from "./definitions/errors.js"; + +export default function getError(code, ctx = {}) { + const { message, details } = ERROR_DEFINITIONS[code](ctx); return new SemanticReleaseError(message, code, details); -}; +} diff --git a/lib/get-fail-comment.js b/lib/get-fail-comment.js index 15b0c00d..6b48bc77 100644 --- a/lib/get-fail-comment.js +++ b/lib/get-fail-comment.js @@ -1,32 +1,36 @@ -const HOME_URL = 'https://github.com/semantic-release/semantic-release'; -const FAQ_URL = `${HOME_URL}/blob/caribou/docs/support/FAQ.md`; +const HOME_URL = "https://github.com/semantic-release/semantic-release"; +const FAQ_URL = `${HOME_URL}/blob/master/docs/support/FAQ.md`; const GET_HELP_URL = `${HOME_URL}#get-help`; -const USAGE_DOC_URL = `${HOME_URL}/blob/caribou/docs/usage/README.md`; +const USAGE_DOC_URL = `${HOME_URL}/blob/master/docs/usage/README.md`; const NEW_ISSUE_URL = `${HOME_URL}/issues/new`; -const formatError = error => `### ${error.message} +const formatError = (error) => `### ${error.message} -${error.details || +${ + error.details || `Unfortunately this error doesn't have any additional information.${ error.pluginName ? ` Feel free to kindly ask the author of the \`${error.pluginName}\` plugin to add more helpful information.` - : '' - }`}`; + : "" + }` +}`; -module.exports = ( - branch, - errors -) => `## :rotating_light: The automated release from the \`${branch}\` branch failed. :rotating_light: +export default function getFailComment(branch, errors) { + return `## :rotating_light: The automated release from the \`${ + branch.name + }\` branch failed. :rotating_light: -I recommend you give this issue a high priority, so other packages depending on you could benefit from your bug fixes and new features. +I recommend you give this issue a high priority, so other packages depending on you can benefit from your bug fixes and new features again. -You can find below the list of errors reported by **semantic-release**. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can resolve this 💪. +You can find below the list of errors reported by **semantic-release**. Each one of them has to be resolved in order to automatically publish your package. I’m sure you can fix this 💪. Errors are usually caused by a misconfiguration or an authentication problem. With each error reported below you will find explanation and guidance to help you to resolve it. -Once all the errors are resolved, **semantic-release** will release your package the next time you push a commit to the \`${branch}\` branch. You can also manually restart the failed CI job that runs **semantic-release**. +Once all the errors are resolved, **semantic-release** will release your package the next time you push a commit to the \`${ + branch.name + }\` branch. You can also manually restart the failed CI job that runs **semantic-release**. -If you are not sure how to resolve this, here is some links that can help you: +If you are not sure how to resolve this, here are some links that can help you: - [Usage documentation](${USAGE_DOC_URL}) - [Frequently Asked Questions](${FAQ_URL}) - [Support channels](${GET_HELP_URL}) @@ -35,10 +39,11 @@ If those don’t help, or if this issue is reporting something you think isn’t --- -${errors.map(formatError).join('\n\n---\n\n')} +${errors.map((error) => formatError(error)).join("\n\n---\n\n")} --- Good luck with your project ✨ Your **[semantic-release](${HOME_URL})** bot :package::rocket:`; +} diff --git a/lib/get-release-links.js b/lib/get-release-links.js new file mode 100644 index 00000000..767bfba1 --- /dev/null +++ b/lib/get-release-links.js @@ -0,0 +1,25 @@ +import { RELEASE_NAME } from "./definitions/constants.js"; + +const linkify = (releaseInfo) => + `${ + releaseInfo.url + ? releaseInfo.url.startsWith("http") + ? `[${releaseInfo.name}](${releaseInfo.url})` + : `${releaseInfo.name}: \`${releaseInfo.url}\`` + : `\`${releaseInfo.name}\`` + }`; + +const filterReleases = (releaseInfos) => + releaseInfos.filter( + (releaseInfo) => releaseInfo.name && releaseInfo.name !== RELEASE_NAME, + ); + +export default function getReleaseLinks(releaseInfos) { + return `${ + filterReleases(releaseInfos).length > 0 + ? `This release is also available on:\n${filterReleases(releaseInfos) + .map((releaseInfo) => `- ${linkify(releaseInfo)}`) + .join("\n")}` + : "" + }`; +} diff --git a/lib/get-search-queries.js b/lib/get-search-queries.js deleted file mode 100644 index 60eb7649..00000000 --- a/lib/get-search-queries.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = (base, commits, separator = '+') => { - return commits.reduce((searches, commit) => { - const lastSearch = searches[searches.length - 1]; - - if (lastSearch && lastSearch.length + commit.length <= 256 - separator.length) { - searches[searches.length - 1] = `${lastSearch}${separator}${commit}`; - } else { - searches.push(`${base}${separator}${commit}`); - } - - return searches; - }, []); -}; diff --git a/lib/get-success-comment.js b/lib/get-success-comment.js index 5c951973..5cb45fdc 100644 --- a/lib/get-success-comment.js +++ b/lib/get-success-comment.js @@ -1,18 +1,25 @@ -const HOME_URL = 'https://github.com/semantic-release/semantic-release'; -const linkify = releaseInfo => - `${releaseInfo.url ? `[${releaseInfo.name}](${releaseInfo.url})` : `\`${releaseInfo.name}\``}`; +const HOME_URL = "https://github.com/semantic-release/semantic-release"; +const linkify = (releaseInfo) => + `${ + releaseInfo.url + ? `[${releaseInfo.name}](${releaseInfo.url})` + : `\`${releaseInfo.name}\`` + }`; -module.exports = (issue, releaseInfos, nextRelease) => - `:tada: This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${ - nextRelease.version - } :tada:${ +export default function getSuccessComment(issue, releaseInfos, nextRelease) { + return `:tada: This ${ + issue.pull_request ? "PR is included" : "issue has been resolved" + } in version ${nextRelease.version} :tada:${ releaseInfos.length > 0 ? `\n\nThe release is available on${ releaseInfos.length === 1 ? ` ${linkify(releaseInfos[0])}` - : `:\n${releaseInfos.map(releaseInfo => `- ${linkify(releaseInfo)}`).join('\n')}` + : `:\n${releaseInfos + .map((releaseInfo) => `- ${linkify(releaseInfo)}`) + .join("\n")}` }` - : '' + : "" } Your **[semantic-release](${HOME_URL})** bot :package::rocket:`; +} diff --git a/lib/glob-assets.js b/lib/glob-assets.js index a308eb14..75297d7f 100644 --- a/lib/glob-assets.js +++ b/lib/glob-assets.js @@ -1,66 +1,75 @@ -const path = require('path'); -const {basename} = require('path'); -const {isPlainObject, castArray, uniqWith, uniq} = require('lodash'); -const dirGlob = require('dir-glob'); -const globby = require('globby'); -const debug = require('debug')('semantic-release:github'); +import { basename, resolve } from "node:path"; -module.exports = async ({cwd}, assets) => - uniqWith( - [] - .concat( - ...(await Promise.all( - assets.map(async asset => { - // Wrap single glob definition in Array - let glob = castArray(isPlainObject(asset) ? asset.path : asset); - // TODO Temporary workaround for https://github.com/mrmlnc/fast-glob/issues/47 - glob = uniq([...(await dirGlob(glob, {cwd})), ...glob]); +import { isPlainObject, castArray, uniqWith, uniq } from "lodash-es"; +import dirGlob from "dir-glob"; +import { globby } from "globby"; +import debugFactory from "debug"; - // Skip solo negated pattern (avoid to include every non js file with `!**/*.js`) - if (glob.length <= 1 && glob[0].startsWith('!')) { - debug( - 'skipping the negated glob %o as its alone in its group and would retrieve a large amount of files', - glob[0] - ); - return []; - } +const debug = debugFactory("semantic-release:github"); + +export default async function globAssets({ cwd }, assets) { + return uniqWith( + ( + await Promise.all( + assets.map(async (asset) => { + // Wrap single glob definition in Array + let glob = castArray(isPlainObject(asset) ? asset.path : asset); + + // TODO Temporary workaround for https://github.com/mrmlnc/fast-glob/issues/47 + glob = uniq([...(await dirGlob(glob, { cwd })), ...glob]); - const globbed = await globby(glob, { - cwd, - expandDirectories: false, // TODO Temporary workaround for https://github.com/mrmlnc/fast-glob/issues/47 - gitignore: false, - dot: true, - onlyFiles: false, - }); + // Skip solo negated pattern (avoid to include every non js file with `!**/*.js`) + if (glob.length <= 1 && glob[0].startsWith("!")) { + debug( + "skipping the negated glob %o as its alone in its group and would retrieve a large amount of files", + glob[0], + ); + return []; + } - if (isPlainObject(asset)) { - if (globbed.length > 1) { - // If asset is an Object with a glob the `path` property that resolve to multiple files, - // Output an Object definition for each file matched and set each one with: - // - `path` of the matched file - // - `name` based on the actual file name (to avoid assets with duplicate `name`) - // - other properties of the original asset definition - return globbed.map(file => ({...asset, path: file, name: basename(file)})); - } + const globbed = await globby(glob, { + cwd, + expandDirectories: false, // TODO Temporary workaround for https://github.com/mrmlnc/fast-glob/issues/47 + dot: true, + onlyFiles: false, + }); - // If asset is an Object, output an Object definition with: - // - `path` of the matched file if there is one, or the original `path` definition (will be considered as a missing file) + if (isPlainObject(asset)) { + if (globbed.length > 1) { + // If asset is an Object with a glob the `path` property that resolve to multiple files, + // Output an Object definition for each file matched and set each one with: + // - `path` of the matched file + // - `name` based on the actual file name (to avoid assets with duplicate `name`) // - other properties of the original asset definition - return {...asset, path: globbed[0] || asset.path}; + return globbed.map((file) => ({ + ...asset, + path: file, + name: basename(file), + })); } - if (globbed.length > 0) { - // If asset is a String definition, output each files matched - return globbed; - } + // If asset is an Object, output an Object definition with: + // - `path` of the matched file if there is one, or the original `path` definition (will be considered as a missing file) + // - other properties of the original asset definition + return { ...asset, path: globbed[0] || asset.path }; + } + + if (globbed.length > 0) { + // If asset is a String definition, output each files matched + return globbed; + } - // If asset is a String definition but no match is found, output the elements of the original glob (each one will be considered as a missing file) - return glob; - }) - // Sort with Object first, to prioritize Object definition over Strings in dedup - )) + // If asset is a String definition but no match is found, output the elements of the original glob (each one will be considered as a missing file) + return glob; + }), + // Sort with Object first, to prioritize Object definition over Strings in dedup ) - .sort(asset => (isPlainObject(asset) ? -1 : 1)), + ) + .flat() + .sort((asset) => (isPlainObject(asset) ? -1 : 1)), // Compare `path` property if Object definition, value itself if String - (a, b) => path.resolve(cwd, isPlainObject(a) ? a.path : a) === path.resolve(cwd, isPlainObject(b) ? b.path : b) + (a, b) => + resolve(cwd, isPlainObject(a) ? a.path : a) === + resolve(cwd, isPlainObject(b) ? b.path : b), ); +} diff --git a/lib/is-prerelease.js b/lib/is-prerelease.js new file mode 100644 index 00000000..c04a7ba3 --- /dev/null +++ b/lib/is-prerelease.js @@ -0,0 +1,11 @@ +export default function isPrerelease({ type, main, prerelease }) { + if (prerelease === false) { + return false; + } + return ( + type === "prerelease" || + (type === "release" && !main) || + typeof prerelease == "string" || + prerelease === true + ); +} diff --git a/lib/octokit.js b/lib/octokit.js new file mode 100644 index 00000000..6bb6db1c --- /dev/null +++ b/lib/octokit.js @@ -0,0 +1,79 @@ +/* c8 ignore start */ +// @ts-check + +import { createRequire } from "node:module"; + +// If maintaining @octokit/core and the separate plugins gets to cumbersome +// then the `octokit` package can be used which has all these plugins included. +// However the `octokit` package has a lot of other things we don't care about. +// We use only the bits we need to minimize the size of the package. +import { Octokit } from "@octokit/core"; +import { paginateRest } from "@octokit/plugin-paginate-rest"; +import { retry } from "@octokit/plugin-retry"; +import { throttling } from "@octokit/plugin-throttling"; +import urljoin from "url-join"; +import { HttpProxyAgent } from "http-proxy-agent"; +import { HttpsProxyAgent } from "https-proxy-agent"; + +import { RETRY_CONF } from "./definitions/retry.js"; +import { THROTTLE_CONF } from "./definitions/throttle.js"; + +// NOTE: replace with import ... assert { type: 'json' } once supported +const require = createRequire(import.meta.url); +const pkg = require("../package.json"); + +const onRetry = (retryAfter, options, octokit, retryCount) => { + octokit.log.warn( + `Request quota exhausted for request ${options.method} ${options.url}`, + ); + + if (retryCount <= RETRY_CONF.retries) { + octokit.log.debug(`Will retry after ${retryAfter}.`); + return true; + } +}; + +export const SemanticReleaseOctokit = Octokit.plugin( + paginateRest, + retry, + throttling, +).defaults({ + userAgent: `@semantic-release/github v${pkg.version}`, + retry: RETRY_CONF, + throttle: { + ...THROTTLE_CONF, + onRateLimit: onRetry, + onSecondaryRateLimit: onRetry, + }, +}); +/* c8 ignore stop */ + +/** + * @param {{githubToken: string, proxy: any} | {githubUrl: string, githubApiPathPrefix: string, githubApiUrl: string,githubToken: string, proxy: any}} options + * @returns {{ auth: string, baseUrl?: string, request: { agent?: any } }} + */ +export function toOctokitOptions(options) { + const baseUrl = + "githubApiUrl" in options && options.githubApiUrl + ? // Use `urljoin` to normalize the provided URL + urljoin(options.githubApiUrl, "") + : "githubUrl" in options && options.githubUrl + ? urljoin(options.githubUrl, options.githubApiPathPrefix) + : undefined; + + const agent = options.proxy + ? baseUrl && new URL(baseUrl).protocol.replace(":", "") === "http" + ? // Some `proxy.headers` need to be passed as second arguments since version 6 or 7 + // For simplicity, we just pass the same proxy object twice. It works 🤷🏻 + new HttpProxyAgent(options.proxy, options.proxy) + : new HttpsProxyAgent(options.proxy, options.proxy) + : undefined; + + return { + ...(baseUrl ? { baseUrl } : {}), + auth: options.githubToken, + request: { + agent, + }, + }; +} diff --git a/lib/parse-github-url.js b/lib/parse-github-url.js index de977f3c..4301798a 100644 --- a/lib/parse-github-url.js +++ b/lib/parse-github-url.js @@ -1,13 +1,19 @@ -const {URL} = require('url'); - -module.exports = repositoryUrl => { - const [match, auth, host, path] = /^(?!.+:\/\/)(?:(.*)@)?(.*?):(.*?)$/.exec(repositoryUrl) || []; +export default function parseGitHubUrl(repositoryUrl) { + const [match, auth, host, path] = + /^(?!.+:\/\/)(?:(?.*)@)?(?.*?):(?.*)$/.exec( + repositoryUrl, + ) || []; try { - const [, owner, repo] = /^\/([^/]+)?\/?(.+?)(?:\.git)?$/.exec( - new URL(match ? `ssh://${auth ? `${auth}@` : ''}${host}/${path}` : repositoryUrl).pathname - ); - return {owner, repo}; - } catch (_) { + const [, owner, repo] = + /^\/(?[^/]+)?\/?(?.+?)(?:\.git)?$/.exec( + new URL( + match + ? `ssh://${auth ? `${auth}@` : ""}${host}/${path}` + : repositoryUrl, + ).pathname, + ); + return { owner, repo }; + } catch { return {}; } -}; +} diff --git a/lib/publish.js b/lib/publish.js index 27d7d28c..b91c39d1 100644 --- a/lib/publish.js +++ b/lib/publish.js @@ -1,113 +1,188 @@ -const {basename, extname, resolve} = require('path'); -const {stat, readFile} = require('fs-extra'); -const {isPlainObject, template} = require('lodash'); -const mime = require('mime'); -const debug = require('debug')('semantic-release:github'); -const parseGithubUrl = require('./parse-github-url'); -const globAssets = require('./glob-assets.js'); -const resolveConfig = require('./resolve-config'); -const getClient = require('./get-client'); - -module.exports = async (pluginConfig, context) => { +import { resolve, basename, extname } from "node:path"; +import { stat, readFile } from "node:fs/promises"; + +import { isPlainObject, template } from "lodash-es"; +import mime from "mime"; +import debugFactory from "debug"; + +import { RELEASE_NAME } from "./definitions/constants.js"; +import parseGithubUrl from "./parse-github-url.js"; +import globAssets from "./glob-assets.js"; +import resolveConfig from "./resolve-config.js"; +import { toOctokitOptions } from "./octokit.js"; +import isPrerelease from "./is-prerelease.js"; + +const debug = debugFactory("semantic-release:github"); + +export default async function publish(pluginConfig, context, { Octokit }) { const { cwd, - options: {branch, repositoryUrl}, - nextRelease: {gitTag, notes}, + options: { repositoryUrl }, + branch, + nextRelease: { gitTag }, logger, } = context; - const {githubToken, githubUrl, githubApiPathPrefix, proxy, assets} = resolveConfig(pluginConfig, context); - const {owner, repo} = parseGithubUrl(repositoryUrl); - const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy}); - - const releaseData = { + const { + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + assets, + draftRelease, + releaseNameTemplate, + releaseBodyTemplate, + discussionCategoryName, + } = resolveConfig(pluginConfig, context); + const { owner, repo } = parseGithubUrl(repositoryUrl); + const octokit = new Octokit( + toOctokitOptions({ + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + }), + ); + const release = { owner, repo, tag_name: gitTag, - name: gitTag, - target_commitish: branch, - body: notes, + target_commitish: branch.name, + name: template(releaseNameTemplate)(context), + body: template(releaseBodyTemplate)(context), + prerelease: isPrerelease(branch), }; - debug('release owner: %o', owner); - debug('release repo: %o', repo); - debug('release name: %o', gitTag); - debug('release branch: %o', branch); + debug("release object: %O", release); + + const draftReleaseOptions = { ...release, draft: true }; - // When there are no assets, we publish a release directly + // When there are no assets, we publish a release directly. if (!assets || assets.length === 0) { + // If draftRelease is true we create a draft release instead. + if (draftRelease) { + const { + data: { html_url: url, id: releaseId }, + } = await octokit.request( + "POST /repos/{owner}/{repo}/releases", + draftReleaseOptions, + ); + + logger.log("Created GitHub draft release: %s", url); + return { url, name: RELEASE_NAME, id: releaseId }; + } + + // add discussion_category_name if discussionCategoryName is not undefined or false + if (discussionCategoryName) { + release.discussion_category_name = discussionCategoryName; + } + const { - data: {html_url: url}, - } = await github.repos.createRelease(releaseData); + data: { html_url: url, id: releaseId, discussion_url }, + } = await octokit.request("POST /repos/{owner}/{repo}/releases", release); - logger.log('Published GitHub release: %s', url); - return {url, name: 'GitHub release'}; + logger.log("Published GitHub release: %s", url); + + if (discussionCategoryName) { + logger.log("Created GitHub release discussion: %s", discussion_url); + } + + return { url, name: RELEASE_NAME, id: releaseId, discussion_url }; } // We'll create a draft release, append the assets to it, and then publish it. // This is so that the assets are available when we get a Github release event. - const draftRelease = {...releaseData, draft: true}; - const { - data: {upload_url: uploadUrl, id: releaseId}, - } = await github.repos.createRelease(draftRelease); + data: { upload_url: uploadUrl, html_url: draftUrl, id: releaseId }, + } = await octokit.request( + "POST /repos/{owner}/{repo}/releases", + draftReleaseOptions, + ); // Append assets to the release const globbedAssets = await globAssets(context, assets); - debug('globed assets: %o', globbedAssets); + debug("globed assets: %o", globbedAssets); await Promise.all( - globbedAssets.map(async asset => { + globbedAssets.map(async (asset) => { const filePath = isPlainObject(asset) ? asset.path : asset; let file; try { file = await stat(resolve(cwd, filePath)); - } catch (_) { - logger.error('The asset %s cannot be read, and will be ignored.', filePath); + } catch { + logger.error( + "The asset %s cannot be read, and will be ignored.", + filePath, + ); return; } if (!file || !file.isFile()) { - logger.error('The asset %s is not a file, and will be ignored.', filePath); + logger.error( + "The asset %s is not a file, and will be ignored.", + filePath, + ); return; } const fileName = template(asset.name || basename(filePath))(context); const upload = { + method: "POST", url: uploadUrl, - file: await readFile(resolve(cwd, filePath)), + data: await readFile(resolve(cwd, filePath)), name: fileName, headers: { - 'content-type': mime.getType(extname(fileName)) || 'text/plain', - 'content-length': file.size, + "content-type": mime.getType(extname(fileName)) || "text/plain", + "content-length": file.size, }, }; - debug('file path: %o', filePath); - debug('file name: %o', fileName); + debug("file path: %o", filePath); + debug("file name: %o", fileName); if (isPlainObject(asset) && asset.label) { upload.label = template(asset.label)(context); } const { - data: {browser_download_url: downloadUrl}, - } = await github.repos.uploadReleaseAsset(upload); - logger.log('Published file %s', downloadUrl); - }) + data: { browser_download_url: downloadUrl }, + } = await octokit.request(upload); + logger.log("Published file %s", downloadUrl); + }), ); - const release = { + // If we want to create a draft we don't need to update the release again + if (draftRelease) { + logger.log("Created GitHub draft release: %s", draftUrl); + return { url: draftUrl, name: RELEASE_NAME, id: releaseId }; + } + + const patchRelease = { owner, repo, release_id: releaseId, draft: false, }; + // add discussion_category_name if discussionCategoryName is not undefined or false + if (discussionCategoryName) { + patchRelease.discussion_category_name = discussionCategoryName; + } + const { - data: {html_url: url}, - } = await github.repos.updateRelease(release); + data: { html_url: url, discussion_url }, + } = await octokit.request( + "PATCH /repos/{owner}/{repo}/releases/{release_id}", + patchRelease, + ); + + logger.log("Published GitHub release: %s", url); + + if (discussionCategoryName) { + logger.log("Created GitHub release discussion: %s", discussion_url); + } - logger.log('Published GitHub release: %s', url); - return {url, name: 'GitHub release'}; -}; + return { url, name: RELEASE_NAME, id: releaseId, discussion_url }; +} diff --git a/lib/resolve-config.js b/lib/resolve-config.js index 6be84e65..578a945e 100644 --- a/lib/resolve-config.js +++ b/lib/resolve-config.js @@ -1,29 +1,66 @@ -const {isNil, castArray} = require('lodash'); +import { isNil, castArray } from "lodash-es"; -module.exports = ( +export default function resolveConfig( { githubUrl, + githubApiUrl, githubApiPathPrefix, proxy, assets, successComment, + successCommentCondition, failTitle, failComment, + failCommentCondition, labels, assignees, releasedLabels, + addReleases, + draftRelease, + releaseNameTemplate, + releaseBodyTemplate, + discussionCategoryName, }, - {env} -) => ({ - githubToken: env.GH_TOKEN || env.GITHUB_TOKEN, - githubUrl: githubUrl || env.GH_URL || env.GITHUB_URL, - githubApiPathPrefix: githubApiPathPrefix || env.GH_PREFIX || env.GITHUB_PREFIX || '', - proxy: proxy || env.HTTP_PROXY, - assets: assets ? castArray(assets) : assets, - successComment, - failTitle: isNil(failTitle) ? 'The automated release is failing 🚨' : failTitle, - failComment, - labels: isNil(labels) ? ['semantic-release'] : labels === false ? false : castArray(labels), - assignees: assignees ? castArray(assignees) : assignees, - releasedLabels: isNil(releasedLabels) ? ['released'] : releasedLabels === false ? false : castArray(releasedLabels), -}); + { env }, +) { + return { + githubToken: env.GH_TOKEN || env.GITHUB_TOKEN, + githubUrl: githubUrl || env.GH_URL || env.GITHUB_URL, + githubApiPathPrefix: + githubApiPathPrefix || env.GH_PREFIX || env.GITHUB_PREFIX || "", + githubApiUrl: githubApiUrl || env.GITHUB_API_URL, + proxy: isNil(proxy) ? env.http_proxy || env.HTTP_PROXY || false : proxy, + assets: assets ? castArray(assets) : assets, + successComment, + successCommentCondition, + failTitle: isNil(failTitle) + ? "The automated release is failing 🚨" + : failTitle, + failComment, + failCommentCondition, + labels: isNil(labels) + ? ["semantic-release"] + : labels === false + ? false + : castArray(labels), + assignees: assignees ? castArray(assignees) : assignees, + releasedLabels: isNil(releasedLabels) + ? [ + `released<%= nextRelease.channel ? \` on @\${nextRelease.channel}\` : "" %>`, + ] + : releasedLabels === false + ? false + : castArray(releasedLabels), + addReleases: isNil(addReleases) ? false : addReleases, + draftRelease: isNil(draftRelease) ? false : draftRelease, + releaseBodyTemplate: !isNil(releaseBodyTemplate) + ? releaseBodyTemplate + : "<%= nextRelease.notes %>", + releaseNameTemplate: !isNil(releaseNameTemplate) + ? releaseNameTemplate + : "<%= nextRelease.name %>", + discussionCategoryName: isNil(discussionCategoryName) + ? false + : discussionCategoryName, + }; +} diff --git a/lib/success.js b/lib/success.js index 6d2e828c..5363863c 100644 --- a/lib/success.js +++ b/lib/success.js @@ -1,19 +1,22 @@ -const {isNil, uniqBy, template, flatten} = require('lodash'); -const pFilter = require('p-filter'); -const AggregateError = require('aggregate-error'); -const issueParser = require('issue-parser'); -const debug = require('debug')('semantic-release:github'); -const parseGithubUrl = require('./parse-github-url'); -const resolveConfig = require('./resolve-config'); -const getClient = require('./get-client'); -const getSearchQueries = require('./get-search-queries'); -const getSuccessComment = require('./get-success-comment'); -const findSRIssues = require('./find-sr-issues'); - -module.exports = async (pluginConfig, context) => { +import { isNil, uniqBy, template, flatten, isEmpty, merge } from "lodash-es"; +import pFilter from "p-filter"; +import AggregateError from "aggregate-error"; +import issueParser from "issue-parser"; +import debugFactory from "debug"; + +import parseGithubUrl from "./parse-github-url.js"; +import resolveConfig from "./resolve-config.js"; +import { toOctokitOptions } from "./octokit.js"; +import getSuccessComment from "./get-success-comment.js"; +import findSRIssues from "./find-sr-issues.js"; +import { RELEASE_NAME } from "./definitions/constants.js"; +import getReleaseLinks from "./get-release-links.js"; + +const debug = debugFactory("semantic-release:github"); + +export default async function success(pluginConfig, context, { Octokit }) { const { - options: {branch, repositoryUrl}, - lastRelease, + options: { repositoryUrl }, commits, nextRelease, releases, @@ -23,125 +26,584 @@ module.exports = async (pluginConfig, context) => { githubToken, githubUrl, githubApiPathPrefix, + githubApiUrl, proxy, + labels, successComment, - failComment, + successCommentCondition, failTitle, + failComment, + failCommentCondition, releasedLabels, + addReleases, } = resolveConfig(pluginConfig, context); - const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy}); + const octokit = new Octokit( + toOctokitOptions({ + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + }), + ); + // In case the repo changed name, get the new `repo`/`owner` as the search API will not follow redirects - const [owner, repo] = (await github.repos.get(parseGithubUrl(repositoryUrl))).data.full_name.split('/'); + const { data: repoData } = await octokit.request( + "GET /repos/{owner}/{repo}", + parseGithubUrl(repositoryUrl), + ); + const [owner, repo] = repoData.full_name.split("/"); const errors = []; - if (successComment === false) { - logger.log('Skip commenting on issues and pull requests.'); + if (successComment === false || isEmpty(commits)) { + if (isEmpty(commits)) { + logger.log("No commits found in release"); + } + logger.log("Skip commenting on issues and pull requests."); + logger.warn( + `DEPRECATION: 'false' for 'successComment' is deprecated and will be removed in a future major version. Use 'successCommentCondition' instead.`, + ); + } else if (successCommentCondition === false) { + logger.log("Skip commenting on issues and pull requests."); } else { - const parser = issueParser('github', githubUrl ? {hosts: [githubUrl]} : {}); - const releaseInfos = releases.filter(release => Boolean(release.name)); - const shas = commits.map(({hash}) => hash); + const parser = issueParser( + "github", + githubUrl ? { hosts: [githubUrl] } : {}, + ); + const releaseInfos = releases.filter((release) => Boolean(release.name)); + const shas = commits.map(({ hash }) => hash); + + // Get associatedPRs + const associatedPRs = await inChunks(shas, 100, async (chunk) => { + const responsePRs = []; + const { repository } = await octokit.graphql( + buildAssociatedPRsQuery(chunk), + { owner, repo }, + ); + const responseAssociatedPRs = Object.values(repository).map( + (item) => item.associatedPullRequests, + ); + for (const { nodes, pageInfo } of responseAssociatedPRs) { + if (nodes.length === 0) continue; + + responsePRs.push(...buildIssuesOrPRsFromResponseNode(nodes)); + if (pageInfo.hasNextPage) { + let cursor = pageInfo.endCursor; + let hasNextPage = true; + while (hasNextPage) { + const { repository } = await octokit.graphql( + loadSingleCommitAssociatedPRs, + { owner, repo, sha: response.commit.oid, cursor }, + ); + const { associatedPullRequests } = repository.commit; + responsePRs.push( + ...buildIssuesOrPRsFromResponseNode( + associatedPullRequests.nodes, + "PR", + ), + ); + if (associatedPullRequests.pageInfo.hasNextPage) { + cursor = associatedPullRequests.pageInfo.endCursor; + } else { + hasNextPage = false; + } + } + } + } + return responsePRs; + }); + + const uniqueAssociatedPRs = uniqBy(flatten(associatedPRs), "number"); - const searchQueries = getSearchQueries(`repo:${owner}/${repo}+type:pr+is:merged`, shas).map( - async q => (await github.search.issuesAndPullRequests({q})).data.items + const prs = await pFilter(uniqueAssociatedPRs, async ({ number }) => { + const commits = await octokit.paginate( + "GET /repos/{owner}/{repo}/pulls/{pull_number}/commits", + { + owner, + repo, + pull_number: number, + }, + ); + const matchingCommit = commits.find(({ sha }) => shas.includes(sha)); + if (matchingCommit) return matchingCommit; + + const { data: pullRequest } = await octokit.request( + "GET /repos/{owner}/{repo}/pulls/{pull_number}", + { + owner, + repo, + pull_number: number, + }, + ); + return shas.includes(pullRequest.merge_commit_sha); + }); + + debug( + "found pull requests: %O", + prs.map((pr) => pr.number), ); - const prs = await pFilter( - uniqBy(flatten(await Promise.all(searchQueries)), 'number'), - async ({number}) => - (await github.pullRequests.listCommits({owner, repo, pull_number: number})).data.find(({sha}) => - shas.includes(sha) - ) || shas.includes((await github.pullRequests.get({owner, repo, pull_number: number})).data.merge_commit_sha) + // Parse the release commits message and PRs body to find resolved issues/PRs via comment keyworkds + const parsedIssues = [ + ...prs.map((pr) => pr.body), + ...commits.map((commit) => commit.message), + ].reduce( + (issues, message) => + message + ? issues.concat( + parser(message) + .actions.close.filter( + (action) => + isNil(action.slug) || action.slug === `${owner}/${repo}`, + ) + .map((action) => ({ + number: Number.parseInt(action.issue, 10), + })), + ) + : issues, + [], ); - debug('found pull requests: %O', prs.map(pr => pr.number)); + let issues = []; - // Parse the release commits message and PRs body to find resolved issues/PRs via comment keyworkds - const issues = [...prs.map(pr => pr.body), ...commits.map(commit => commit.message)].reduce((issues, message) => { - return message - ? issues.concat( - parser(message) - .actions.close.filter(action => isNil(action.slug) || action.slug === `${owner}/${repo}`) - .map(action => ({number: parseInt(action.issue, 10)})) - ) - : issues; - }, []); - - debug('found issues via comments: %O', issues); + if (!isEmpty(parsedIssues)) { + const uniqueParsedIssues = uniqBy(flatten(parsedIssues), "number"); + + // Get relatedIssues (or relatedPRs i.e. Issues/PRs that are closed by an associatedPR) + issues = await inChunks(uniqueParsedIssues, 100, async (chunk) => { + const { repository } = await octokit.graphql( + buildRelatedIssuesQuery(chunk.map((issue) => issue.number)), + { owner, repo }, + ); + const responseRelatedIssues = Object.values(repository).map( + (issue) => issue, + ); + return buildIssuesOrPRsFromResponseNode(responseRelatedIssues); + }); + + debug( + "found related issues via PRs and Commits: %O", + issues.map((issue) => issue.number), + ); + } await Promise.all( - uniqBy([...prs, ...issues], 'number').map(async issue => { + uniqBy([...prs, ...issues], "number").map(async (issue) => { + const issueOrPR = issue.pull_request ? "PR" : "issue"; + + const canCommentOnIssue = successCommentCondition + ? template(successCommentCondition)({ ...context, issue }) + : true; + + if (!canCommentOnIssue) { + logger.log(`Skip commenting on ${issueOrPR} #%d.`, issue.number); + return; + } + const body = successComment - ? template(successComment)({branch, lastRelease, commits, nextRelease, releases, issue}) + ? template(successComment)({ ...context, issue }) : getSuccessComment(issue, releaseInfos, nextRelease); try { - const state = issue.state || (await github.issues.get({owner, repo, issue_number: issue.number})).data.state; - - if (state === 'closed') { - const comment = {owner, repo, issue_number: issue.number, body}; - debug('create comment: %O', comment); - const { - data: {html_url: url}, - } = await github.issues.createComment(comment); - logger.log('Added comment to issue #%d: %s', issue.number, url); - - if (releasedLabels) { - // Don’t use .issues.addLabels for GHE < 2.16 support - // https://github.com/semantic-release/github/issues/138 - await github.request('POST /repos/:owner/:repo/issues/:number/labels', { + const comment = { owner, repo, issue_number: issue.number, body }; + debug("create comment: %O", comment); + const { + data: { html_url: url }, + } = await octokit.request( + "POST /repos/{owner}/{repo}/issues/{issue_number}/comments", + comment, + ); + logger.log( + `Added comment to ${issueOrPR} #%d: %s`, + issue.number, + url, + ); + + if (releasedLabels) { + const labels = releasedLabels.map((label) => + template(label)(context), + ); + await octokit.request( + "POST /repos/{owner}/{repo}/issues/{issue_number}/labels", + { owner, repo, - number: issue.number, - data: releasedLabels, - }); - logger.log('Added labels %O to issue #%d', releasedLabels, issue.number); - } - } else { - logger.log("Skip comment and labels on issue #%d as it's open: %s", issue.number); + issue_number: issue.number, + data: labels, + }, + ); + logger.log( + `Added labels %O to ${issueOrPR} #%d`, + labels, + issue.number, + ); } } catch (error) { if (error.status === 403) { - logger.error('Not allowed to add a comment to the issue #%d.', issue.number); + logger.error( + `Not allowed to add a comment to the issue/PR #%d.`, + issue.number, + ); } else if (error.status === 404) { - logger.error("Failed to add a comment to the issue #%d as it doesn't exist.", issue.number); + logger.error( + `Failed to add a comment to the issue/PR #%d as it doesn't exist.`, + issue.number, + ); } else { errors.push(error); - logger.error('Failed to add a comment to the issue #%d.', issue.number); + logger.error( + `Failed to add a comment to the issue/PR #%d.`, + issue.number, + ); // Don't throw right away and continue to update other issues } } - }) + }), ); } if (failComment === false || failTitle === false) { - logger.log('Skip closing issue.'); + logger.log("Skip closing issue."); } else { - const srIssues = await findSRIssues(github, failTitle, owner, repo); + const srIssues = await findSRIssues( + octokit, + logger, + failTitle, + labels, + owner, + repo, + ); - debug('found semantic-release issues: %O', srIssues); + debug("found semantic-release issues: %O", srIssues); await Promise.all( - srIssues.map(async issue => { - debug('close issue: %O', issue); + srIssues.map(async (issue) => { + debug("close issue: %O", issue); try { - const updateIssue = {owner, repo, issue_number: issue.number, state: 'closed'}; - debug('closing issue: %O', updateIssue); + const updateIssue = { + owner, + repo, + issue_number: issue.number, + state: "closed", + }; + debug("closing issue: %O", updateIssue); const { - data: {html_url: url}, - } = await github.issues.update(updateIssue); - logger.log('Closed issue #%d: %s.', issue.number, url); + data: { html_url: url }, + } = await octokit.request( + "PATCH /repos/{owner}/{repo}/issues/{issue_number}", + updateIssue, + ); + logger.log("Closed issue #%d: %s.", issue.number, url); } catch (error) { errors.push(error); - logger.error('Failed to close the issue #%d.', issue.number); + logger.error("Failed to close the issue #%d.", issue.number); // Don't throw right away and continue to close other issues } - }) + }), + ); + } + + if (addReleases !== false && errors.length === 0) { + const ghRelease = releases.find( + (release) => release.name && release.name === RELEASE_NAME, ); + if (!isNil(ghRelease)) { + const ghRelaseId = ghRelease.id; + const additionalReleases = getReleaseLinks(releases); + if (!isEmpty(additionalReleases) && !isNil(ghRelaseId)) { + const newBody = + addReleases === "top" + ? additionalReleases.concat("\n---\n", nextRelease.notes) + : nextRelease.notes.concat("\n---\n", additionalReleases); + await octokit.request( + "PATCH /repos/{owner}/{repo}/releases/{release_id}", + { + owner, + repo, + release_id: ghRelaseId, + body: newBody, + }, + ); + } + } } if (errors.length > 0) { throw new AggregateError(errors); } -}; +} + +/** + * In order to speed up a function call that handles a big array of items, we split up the + * array in chunks and call the function for each chunk in parallel. At the end we combine the + * results again. + * + * @template TItem + * @template TCallbackResult + * @param {TItem[]} items + * @param {number} chunkSize + * @param {(items: TItem[]) => TCallbackResult} callback + * @returns TCallbackResult + */ +async function inChunks(items, chunkSize, callback) { + const chunkCalls = []; + for (let i = 0; i < items.length; i += chunkSize) { + chunkCalls.push(callback(items.slice(i, i + chunkSize))); + } + const results = await Promise.all(chunkCalls); + + return results.flat(); +} + +/** + * Fields common accross PRs and Issue + */ +const baseFields = ` + __typename + id + title + body + url + number + createdAt + updatedAt + closedAt + comments { + totalCount + } + state + author { + login + url + avatarUrl + __typename + } + authorAssociation + activeLockReason + labels(first: 40) { + nodes { + id + url + name + color + description + isDefault + } + } + milestone { + url + id + number + state + title + description + creator { + login + url + avatarUrl + } + createdAt + closedAt + updatedAt + } + locked +`; + +/** + * Builds GraphQL query for fetching PRs/Commits related Issues to a list of commit hash (sha) + * @param {Array} numbers + * @returns {string} + */ +function buildRelatedIssuesQuery(numbers) { + return `#graphql + query getRelatedIssues($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + ${numbers + .map((num) => { + return `issue${num}: issueOrPullRequest(number: ${num}) { + ... on Issue { + ${baseFields} + } + ... on PullRequest { + ${baseFields} + mergeable + changedFiles + mergedAt + isDraft + mergedBy { + login + avatarUrl + url + } + commits { + totalCount + } + } + }`; + }) + .join("")} + } + } + `; +} + +/** + * Builds GraphQL query for fetching associated PRs to a list of commit hash (sha) + * @param {Array} shas + * @returns {string} + */ +function buildAssociatedPRsQuery(shas) { + return `#graphql + query getAssociatedPRs($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + ${shas + .map((sha) => { + return `commit${sha.slice(0, 6)}: object(oid: "${sha}") { + ...on Commit { + oid + associatedPullRequests(first: 100) { + pageInfo { + endCursor + hasNextPage + } + nodes { + ${baseFields} + mergeable + changedFiles + mergedAt + isDraft + mergedBy { + login + avatarUrl + url + } + commits { + totalCount + } + } + } + } + }`; + }) + .join("")} + } + } + `; +} + +/** + * GraphQL Query to fetch additional associatedPR for commits that has more than 100 associatedPRs + */ +const loadSingleCommitAssociatedPRs = `#graphql + query getCommitAssociatedPRs($owner: String!, $repo: String!, $sha: String!, $cursor: String) { + repository(owner: $owner, name: $repo) { + commit: object(oid: $sha) { + ...on Commit { + oid + associatedPullRequests(after: $cursor, first: 100) { + pageInfo { + endCursor + hasNextPage + } + nodes { + ${baseFields} + mergeable + changedFiles + mergedAt + isDraft + mergedBy { + login + avatarUrl + url + } + commits { + totalCount + } + } + } + } + } + } + } +`; + +/** + * Build associatedPRs or RelatedIssues object (into issue-like object with `pull_request` property) from the GraphQL repository response + * @param {object} responseNodes + * @returns {object[]} + */ +function buildIssuesOrPRsFromResponseNode(responseNodes) { + const resultArray = []; + for (const node of responseNodes) { + let baseProps = { + number: node.number, + title: node.title, + body: node.body, + labels: node.labels?.nodes.map((label) => { + return { + id: label.id, + url: label.url, + name: label.name, + color: label.color, + description: label.description, + default: label.isDefault, + }; + }), + html_url: node.url, + created_at: node.createdAt, + updated_at: node.updatedAt, + user: { + login: node.author?.login, + html_url: node.author?.url, + avatar_url: node.author?.avatarUrl, + type: node.author?.__typename, + }, + comments: node.comments?.totalCount, + state: node.state, + milestone: node.milestone + ? { + url: node.milestone.url, + id: node.milestone.id, + number: node.milestone.number, + state: node.milestone.state, + title: node.milestone.title, + description: node.milestone.description, + creator: { + login: node.milestone.creator.login, + html_url: node.milestone.creator.url, + avatar_url: node.milestone.creator.avatarUrl, + }, + created_at: node.milestone.createdAt, + closed_at: node.milestone.closedAt, + updated_at: node.milestone.updatedAt, + } + : null, + locked: node.locked, + active_lock_reason: node.activeLockReason, + closed_at: node.closedAt, + }; + + let result = baseProps; + + if (node.__typename === "PullRequest") { + const prProps = { + pull_request: true, + mergeable: node.mergeable, + changed_files: node.changedFiles, + commits: node.commits?.totalCount, + merged_at: node.mergedAt, + draft: node.isDraft, + merged_by: { + login: node.mergedBy?.login, + avatar_url: node.mergedBy?.avatarUrl, + html_url: node.mergedBy?.url, + }, + }; + result = merge(baseProps, prProps); + } + + resultArray.push(result); + } + return resultArray; +} diff --git a/lib/verify.js b/lib/verify.js index a21c7dea..3f348809 100644 --- a/lib/verify.js +++ b/lib/verify.js @@ -1,80 +1,147 @@ -const {isString, isPlainObject, isNil, isArray, isNumber} = require('lodash'); -const urlJoin = require('url-join'); -const AggregateError = require('aggregate-error'); -const parseGithubUrl = require('./parse-github-url'); -const resolveConfig = require('./resolve-config'); -const getClient = require('./get-client'); -const getError = require('./get-error'); +import { + isString, + isPlainObject, + isNil, + isArray, + isNumber, + isBoolean, +} from "lodash-es"; +import urlJoin from "url-join"; +import AggregateError from "aggregate-error"; -const isNonEmptyString = value => isString(value) && value.trim(); -const isStringOrStringArray = value => isNonEmptyString(value) || (isArray(value) && value.every(isNonEmptyString)); -const isArrayOf = validator => array => isArray(array) && array.every(value => validator(value)); -const canBeDisabled = validator => value => value === false || validator(value); +import parseGithubUrl from "./parse-github-url.js"; +import resolveConfig from "./resolve-config.js"; +import { toOctokitOptions } from "./octokit.js"; +import getError from "./get-error.js"; + +const isNonEmptyString = (value) => isString(value) && value.trim(); +const oneOf = (enumArray) => (value) => enumArray.includes(value); +const isStringOrStringArray = (value) => + isNonEmptyString(value) || + (isArray(value) && value.every((string) => isNonEmptyString(string))); +const isArrayOf = (validator) => (array) => + isArray(array) && array.every((value) => validator(value)); +const canBeDisabled = (validator) => (value) => + value === false || validator(value); const VALIDATORS = { - proxy: proxy => - isNonEmptyString(proxy) || (isPlainObject(proxy) && isNonEmptyString(proxy.host) && isNumber(proxy.port)), + proxy: canBeDisabled( + (proxy) => + isNonEmptyString(proxy) || + (isPlainObject(proxy) && + isNonEmptyString(proxy.host) && + isNumber(proxy.port)), + ), assets: isArrayOf( - asset => isStringOrStringArray(asset) || (isPlainObject(asset) && isStringOrStringArray(asset.path)) + (asset) => + isStringOrStringArray(asset) || + (isPlainObject(asset) && isStringOrStringArray(asset.path)), ), successComment: canBeDisabled(isNonEmptyString), + successCommentCondition: canBeDisabled(isNonEmptyString), failTitle: canBeDisabled(isNonEmptyString), failComment: canBeDisabled(isNonEmptyString), + failCommentCondition: canBeDisabled(isNonEmptyString), labels: canBeDisabled(isArrayOf(isNonEmptyString)), assignees: isArrayOf(isNonEmptyString), releasedLabels: canBeDisabled(isArrayOf(isNonEmptyString)), + addReleases: canBeDisabled(oneOf(["bottom", "top"])), + draftRelease: isBoolean, + releaseBodyTemplate: isNonEmptyString, + releaseNameTemplate: isNonEmptyString, + discussionCategoryName: canBeDisabled(isNonEmptyString), }; -module.exports = async (pluginConfig, context) => { +export default async function verify(pluginConfig, context, { Octokit }) { const { env, - options: {repositoryUrl}, + options: { repositoryUrl }, logger, } = context; - const {githubToken, githubUrl, githubApiPathPrefix, proxy, ...options} = resolveConfig(pluginConfig, context); + const { + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + ...options + } = resolveConfig(pluginConfig, context); - const errors = Object.entries({...options, proxy}).reduce( + const errors = Object.entries({ ...options, proxy }).reduce( (errors, [option, value]) => !isNil(value) && !VALIDATORS[option](value) - ? [...errors, getError(`EINVALID${option.toUpperCase()}`, {[option]: value})] + ? [ + ...errors, + getError(`EINVALID${option.toUpperCase()}`, { [option]: value }), + ] : errors, - [] + [], ); - if (githubUrl) { - logger.log('Verify GitHub authentication (%s)', urlJoin(githubUrl, githubApiPathPrefix)); + if (githubApiUrl) { + logger.log("Verify GitHub authentication (%s)", githubApiUrl); + } else if (githubUrl) { + logger.log( + "Verify GitHub authentication (%s)", + urlJoin(githubUrl, githubApiPathPrefix), + ); } else { - logger.log('Verify GitHub authentication'); + logger.log("Verify GitHub authentication"); } - const {repo, owner} = parseGithubUrl(repositoryUrl); + const { repo, owner } = parseGithubUrl(repositoryUrl); if (!owner || !repo) { - errors.push(getError('EINVALIDGITHUBURL')); - } else if (githubToken && !errors.find(({code}) => code === 'EINVALIDPROXY')) { - const github = getClient({githubToken, githubUrl, githubApiPathPrefix, proxy}); - - // https://github.com/semantic-release/github/issues/182 - // Do not check for permissions in GitHub actions, as the provided token is an installation access token. - // github.repos.get({repo, owner}) does not return the "permissions" key in that case. But GitHub Actions - // have all permissions required for @semantic-release/github to work - if (env.GITHUB_ACTION) { - return; - } - + errors.push(getError("EINVALIDGITHUBURL")); + } else if ( + githubToken && + !errors.find(({ code }) => code === "EINVALIDPROXY") + ) { + const octokit = new Octokit( + toOctokitOptions({ + githubToken, + githubUrl, + githubApiPathPrefix, + githubApiUrl, + proxy, + }), + ); try { const { - data: { - permissions: {push}, - }, - } = await github.repos.get({repo, owner}); - if (!push) { - errors.push(getError('EGHNOPERMISSION', {owner, repo})); + data: { permissions, clone_url }, + } = await octokit.request("GET /repos/{owner}/{repo}", { repo, owner }); + // Verify if Repository Name wasn't changed + const parsedCloneUrl = parseGithubUrl(clone_url); + if ( + `${owner}/${repo}`.toLowerCase() !== + `${parsedCloneUrl.owner}/${parsedCloneUrl.repo}`.toLowerCase() + ) { + errors.push( + getError("EMISMATCHGITHUBURL", { repositoryUrl, clone_url }), + ); + } + + // https://github.com/semantic-release/github/issues/182 + // Do not check for permissions in GitHub actions, as the provided token is an installation access token. + // octokit.request("GET /repos/{owner}/{repo}", {repo, owner}) does not return the "permissions" key in that case. + // But GitHub Actions have all permissions required for @semantic-release/github to work + if (!env.GITHUB_ACTION && !permissions?.push) { + // If authenticated as GitHub App installation, `push` will always be false. + // We send another request to check if current authentication is an installation. + // Note: we cannot check if the installation has all required permissions, it's + // up to the user to make sure it has + if ( + !(await octokit + .request("HEAD /installation/repositories", { per_page: 1 }) + .catch(() => false)) + ) { + errors.push(getError("EGHNOPERMISSION", { owner, repo })); + } } } catch (error) { if (error.status === 401) { - errors.push(getError('EINVALIDGHTOKEN', {owner, repo})); + errors.push(getError("EINVALIDGHTOKEN", { owner, repo })); } else if (error.status === 404) { - errors.push(getError('EMISSINGREPO', {owner, repo})); + errors.push(getError("EMISSINGREPO", { owner, repo })); } else { throw error; } @@ -82,10 +149,10 @@ module.exports = async (pluginConfig, context) => { } if (!githubToken) { - errors.push(getError('ENOGHTOKEN', {owner, repo})); + errors.push(getError("ENOGHTOKEN", { owner, repo })); } if (errors.length > 0) { throw new AggregateError(errors); } -}; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..99a7dc54 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,15962 @@ +{ + "name": "@semantic-release/github", + "version": "0.0.0-development", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@semantic-release/github", + "version": "0.0.0-development", + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.0", + "@octokit/plugin-paginate-rest": "^13.0.0", + "@octokit/plugin-retry": "^8.0.0", + "@octokit/plugin-throttling": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "devDependencies": { + "ava": "6.4.0", + "c8": "10.1.3", + "cpy": "11.1.0", + "cz-conventional-changelog": "3.3.0", + "fetch-mock": "npm:@gr2m/fetch-mock@9.11.0-pull-request-644.1", + "lockfile-lint": "4.14.1", + "ls-engines": "0.9.3", + "npm-run-all2": "8.0.4", + "prettier": "3.5.3", + "publint": "0.3.12", + "semantic-release": "24.2.5", + "sinon": "21.0.0", + "tempy": "3.1.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.8.1.tgz", + "integrity": "sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/types": "^19.8.1", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.8.1.tgz", + "integrity": "sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.8.1.tgz", + "integrity": "sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/execute-rule": "^19.8.1", + "@commitlint/resolve-extends": "^19.8.1", + "@commitlint/types": "^19.8.1", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^6.1.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.8.1.tgz", + "integrity": "sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@commitlint/config-validator": "^19.8.1", + "@commitlint/types": "^19.8.1", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types": { + "version": "19.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.8.1.tgz", + "integrity": "sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@gar/promisify": { + "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/@isaacs/cliui": { + "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", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "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", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "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", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "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" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/string-locale-compare": { + "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": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/arborist": { + "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", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.2", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^5.0.0", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^4.0.0", + "@npmcli/query": "^3.1.0", + "@npmcli/run-script": "^6.0.0", + "bin-links": "^4.0.1", + "cacache": "^17.0.4", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^6.1.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^9.0.0", + "nopt": "^7.0.0", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-registry-fetch": "^14.0.3", + "npmlog": "^7.0.1", + "pacote": "^15.0.8", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.2", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.1", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/abbrev": { + "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" + } + }, + "node_modules/@npmcli/arborist/node_modules/nopt": { + "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" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/fs": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "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", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "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" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "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" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "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", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/map-workspaces": { + "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", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "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", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^15.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "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", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/name-from-folder": { + "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" + } + }, + "node_modules/@npmcli/node-gyp": { + "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" + } + }, + "node_modules/@npmcli/package-json": { + "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", + "glob": "^10.2.2", + "hosted-git-info": "^6.1.1", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "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" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/query": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "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", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "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" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", + "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.2", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.0.1.tgz", + "integrity": "sha512-m1KvHlueScy4mQJWvFDCxFBTIdXS0K1SgFGLmqHyX90mZdCIv6gWBbKRhatxRjhGlONuTK/hztYdaqrTXcFZdQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.0.1.tgz", + "integrity": "sha512-KUoYR77BjF5O3zcwDQHRRZsUvJwepobeqiSSdCJ8lWt27FZExzb0GgVxrhhfuyF6z2B2zpO0hN5pteni1sqWiw==", + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=7" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.1.tgz", + "integrity": "sha512-S+EVhy52D/272L7up58dr3FNSMXWuNZolkL4zMJBNIfIxyZuUcczsQAU4b5w6dewJXnKYVgSHSV5wxitMSW1kw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": "^7.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", + "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "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": { + "node": ">=14" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true, + "license": "ISC" + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", + "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@publint/pack": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.2.tgz", + "integrity": "sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://bjornlu.com/sponsor" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.1.tgz", + "integrity": "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-11.0.3.tgz", + "integrity": "sha512-T2fKUyFkHHkUNa5XNmcsEcDPuG23hwBKptfUVcFXDVG2cSjXXZYDOfVYwfouqbWo/8UefotLaoGfQeK+k3ep6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.0", + "@octokit/plugin-paginate-rest": "^13.0.0", + "@octokit/plugin-retry": "^8.0.0", + "@octokit/plugin-throttling": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" + } + }, + "node_modules/@semantic-release/npm": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.1.tgz", + "integrity": "sha512-/6nntGSUGK2aTOI0rHPwY3ZjgY9FkXmEHbW9Kr+62NVOsyqpKKeP0lrCH+tphv+EsNdJNmqqwijTEnVWUMQ2Nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.3.tgz", + "integrity": "sha512-XxAZRPWGwO5JwJtS83bRdoIhCiYIx8Vhr+u231pQAsdFIAbm19rSVJLdnBN+Avvk7CKvNQE/nJ4y7uqKH6WTiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^2.0.0", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sigstore/bundle": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "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" + } + }, + "node_modules/@sigstore/sign": { + "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", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@sigstore/sign/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", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@sigstore/sign/node_modules/https-proxy-agent": { + "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", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@sigstore/sign/node_modules/lru-cache": { + "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" + } + }, + "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "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", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign/node_modules/minipass": { + "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" + } + }, + "node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "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", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@sigstore/sign/node_modules/minipass-fetch/node_modules/minipass": { + "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" + } + }, + "node_modules/@sigstore/tuf": { + "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", + "tuf-js": "^1.1.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.2.tgz", + "integrity": "sha512-v46t/fwnhejRSFTGqbpn9u+LQ9xJDse10gNnPgAcxgdoCDMXj/G2asWAC/8Qs+BAZDicX+MNZouXT1A7c83kVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "lodash.get": "^4.4.2", + "type-detect": "^4.1.0" + } + }, + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@tootallnate/once": { + "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" + } + }, + "node_modules/@tufjs/canonical-json": { + "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" + } + }, + "node_modules/@tufjs/models": { + "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", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz", + "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", + "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vercel/nft": { + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.29.4.tgz", + "integrity": "sha512-6lLqMNX3TuycBPABycx7A9F1bHQR7kiQln6abjFbPrf5C/05qHM9M5E4PeTE59c7z8g6vHnx1Ioihb2AQl7BTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^10.4.5", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.3.tgz", + "integrity": "sha512-mQZgUSgFurUtA07ceMjxrWkYz8QtDuYkvPlu0ZqncgjopQ0t6CNEo/OSealkmnagSUx8ZD5ewvezUwUuMqutQg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@yarnpkg/parsers/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "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" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "license": "MIT", + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "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" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "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" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/aproba": { + "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": { + "version": "4.0.2", + "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" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.map": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/array.prototype.map/-/array.prototype.map-1.0.8.tgz", + "integrity": "sha512-YocPM7bYYu2hXGxWpb5vwZ8cMeudNHYtYBcUDY4Z1GWa53qcnQMWSl25jeBHNzitjl9HW2AWW4ro/S/nftUaOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-array-method-boxes-properly": "^1.0.0", + "es-object-atoms": "^1.0.0", + "is-string": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.some": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/array.prototype.some/-/array.prototype.some-1.1.6.tgz", + "integrity": "sha512-GPTqp68jyN2v2fWRPiRqJdtBusmMjFnq/vJSXnuZSyTJINo+7v897s2IuhNHeyOpNz+FoXYdJaL+06U3fxdmRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrgv": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arrgv/-/arrgv-1.0.2.tgz", + "integrity": "sha512-a4eg4yhp7mmruZDQFqVMlxNRFGi/i1r87pt8SDHy0/I8PqSXoUTlWZRdAZo0VXgvEARcujbtTk8kiZRi1uDGRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/arrify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", + "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/ava": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-6.4.0.tgz", + "integrity": "sha512-aeFapuBZtaGwVMlFFf074SZJ0bPcdmAdJdsvhHMp+XaOnC2DgeMzopb7yyYAhulNGRJQfUK/SIBYo2PoX7+gtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vercel/nft": "^0.29.4", + "acorn": "^8.14.1", + "acorn-walk": "^8.3.4", + "ansi-styles": "^6.2.1", + "arrgv": "^1.0.2", + "arrify": "^3.0.0", + "callsites": "^4.2.0", + "cbor": "^10.0.3", + "chalk": "^5.4.1", + "chunkd": "^2.0.1", + "ci-info": "^4.2.0", + "ci-parallel-vars": "^1.0.1", + "cli-truncate": "^4.0.0", + "code-excerpt": "^4.0.0", + "common-path-prefix": "^3.0.0", + "concordance": "^5.0.4", + "currently-unhandled": "^0.4.1", + "debug": "^4.4.1", + "emittery": "^1.1.0", + "figures": "^6.1.0", + "globby": "^14.1.0", + "ignore-by-default": "^2.1.0", + "indent-string": "^5.0.0", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "matcher": "^5.0.0", + "memoize": "^10.1.0", + "ms": "^2.1.3", + "p-map": "^7.0.3", + "package-config": "^5.0.0", + "picomatch": "^4.0.2", + "plur": "^5.1.0", + "pretty-ms": "^9.2.0", + "resolve-cwd": "^3.0.0", + "stack-utils": "^2.0.6", + "strip-ansi": "^7.1.0", + "supertap": "^3.0.1", + "temp-dir": "^3.0.0", + "write-file-atomic": "^6.0.0", + "yargs": "^17.7.2" + }, + "bin": { + "ava": "entrypoints/cli.mjs" + }, + "engines": { + "node": "^18.18 || ^20.8 || ^22 || ^23 || >=24" + }, + "peerDependencies": { + "@ava/typescript": "*" + }, + "peerDependenciesMeta": { + "@ava/typescript": { + "optional": true + } + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "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": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/bin-links": { + "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", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/bin-links/node_modules/write-file-atomic": { + "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", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blueimp-md5": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", + "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "license": "MIT" + }, + "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/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/cacache": { + "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", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/aggregate-error": { + "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", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/chownr": { + "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" + } + }, + "node_modules/cacache/node_modules/clean-stack": { + "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" + } + }, + "node_modules/cacache/node_modules/indent-string": { + "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" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "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" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/p-map": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacache/node_modules/tar": { + "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", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/tar/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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cacache/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/tar/node_modules/minipass": { + "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" + } + }, + "node_modules/cacache/node_modules/yallist": { + "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/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-4.2.0.tgz", + "integrity": "sha512-kfzR4zzQtAE9PC7CzZsjl3aBNbXWuXiSeOCdLcPpBfGW8YuCqQHcRPFDbr/BPVmd3EEPVpuFzLyuT/cUhPr4OQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001723", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", + "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cbor": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-10.0.3.tgz", + "integrity": "sha512-72Jnj81xMsqepqdcSdf2+fflz/UDsThOHy5hj2MW5F5xzHL8Oa0KQ6I6V9CwVUPxg5pf+W9xp6W2KilaRXWWtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nofilter": "^3.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chownr": { + "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" + } + }, + "node_modules/chunkd": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-2.0.1.tgz", + "integrity": "sha512-7d58XsFmOq0j6el67Ug9mHf9ELUXsQXYJBkyxhH/k+6Ke0qXRnv0kbemx+Twc6fRJ07C49lcbdgm9FL1Ei/6SQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "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", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-parallel-vars": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.1.tgz", + "integrity": "sha512-uvzpYrpmidaoxvIQHM+rKSrigjOe9feHYbw4uOI2gdfe1C3xIlxO+kVXq83WQWNniTf8bAxVpy+cQeFQsMERKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/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/cli-highlight/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/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/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/cli-highlight/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/cli-highlight/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/cli-highlight/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/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/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/cli-table3/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/cli-table3/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/cli-table3/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/cli-table3/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/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "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/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cmd-shim": { + "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" + } + }, + "node_modules/code-excerpt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", + "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-to-spaces": "^2.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/color-convert": { + "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" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "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" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/commitizen": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.1.tgz", + "integrity": "sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commitizen/node_modules/brace-expansion": { + "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", + "concat-map": "0.0.1" + } + }, + "node_modules/commitizen/node_modules/glob": { + "version": "7.2.3", + "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", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/commitizen/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/common-ancestor-path": { + "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": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", + "dev": true, + "license": "ISC" + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "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": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", + "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "date-time": "^3.1.0", + "esutils": "^2.0.3", + "fast-diff": "^1.2.0", + "js-string-escape": "^1.0.1", + "lodash": "^4.17.15", + "md5-hex": "^3.0.1", + "semver": "^7.3.2", + "well-known-symbols": "^2.0.0" + }, + "engines": { + "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/console-control-strings": { + "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": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.1.0.tgz", + "integrity": "sha512-dpC440QnORNCO81XYuRRFOLCsjKj4W7tMkUIn3lR6F/FAaJcWLi7iCj6IcEvSQY2zw6VUgwUKd5DEHKEWrpmEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", + "dev": true, + "license": "ISC" + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.2.0.tgz", + "integrity": "sha512-uLnoLeIW4XaoFtH37qEcg/SXMJmKF4vi7V0H2rnPueg+VEtFGA/asSCNTcq4M/GQ6QmlzchAEtOoDTtKqWeHag==", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-to-spaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", + "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/copy-file": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-file/-/copy-file-11.0.0.tgz", + "integrity": "sha512-mFsNh/DIANLqFt5VHZoGirdg7bK5+oTWlhnGu6tgRhzBlnEKWaPX2xrFaLltii/6rmhqFMJqffUgknuRdpYlHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.11", + "p-event": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js": { + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.43.0.tgz", + "integrity": "sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz", + "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "jiti": "^2.4.1" + }, + "engines": { + "node": ">=v18" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=9", + "typescript": ">=5" + } + }, + "node_modules/cpy": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/cpy/-/cpy-11.1.0.tgz", + "integrity": "sha512-QGHetPSSuprVs+lJmMDcivvrBwTKASzXQ5qxFvRC2RFESjjod71bDvFvhxTjDgkNjrrb72AI6JPjfYwxrIy33A==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-file": "^11.0.0", + "globby": "^14.0.2", + "junk": "^4.0.1", + "micromatch": "^4.0.7", + "p-filter": "^4.1.0", + "p-map": "^7.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cssesc": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", + "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" + } + }, + "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cz-conventional-changelog/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cz-conventional-changelog/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", + "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "time-zone": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "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-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "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/electron-to-chromium": { + "version": "1.5.167", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", + "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.1.0.tgz", + "integrity": "sha512-rsX7ktqARv/6UQDgMaLfIqUWAEzzbCQiVh7V9rhDXp6c37yoJcks12NVD+XPkgl4AEavmNhVfrhGoqYwIsMYYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "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": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "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": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/env-ci": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.1.1.tgz", + "integrity": "sha512-mT3ks8F0kwpo7SYNds6nWj0PaRh+qJxIeBVBXAKTN9hphAzZv7s0QAZQbqnB1fAv/r4pJUGE15BV9UrS31FP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/env-ci/node_modules/execa": { + "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", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/env-ci/node_modules/get-stream": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/human-signals": { + "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" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/mimic-fn": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/npm-run-path": { + "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" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/onetime": { + "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" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/path-key": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "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" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "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": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exponential-backoff": { + "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/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast_array_intersect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast_array_intersect/-/fast_array_intersect-1.1.0.tgz", + "integrity": "sha512-/DCilZlUdz2XyNDF+ASs0PwY+RKG9Y4Silp/gbS72Cvbg4oibc778xcecg+pnNyiNHYgh/TApsiDTjpdniyShw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fetch-mock": { + "name": "@gr2m/fetch-mock", + "version": "9.11.0-pull-request-644.1", + "resolved": "https://registry.npmjs.org/@gr2m/fetch-mock/-/fetch-mock-9.11.0-pull-request-644.1.tgz", + "integrity": "sha512-gTp6RCHzlOXS1qRb0APfuyz48Lw/JFPa4uiar+kEgL1STsDwth75HJZ4x30tBlXMJXV8XDTDzJ2Hz9w3RWiHJA==", + "deprecated": "The fix has been implemented in fetch-mock@10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.0.0", + "@babel/runtime": "^7.0.0", + "core-js": "^3.0.0", + "debug": "^4.1.1", + "glob-to-regexp": "^0.4.0", + "is-subset": "^0.1.1", + "lodash.isequal": "^4.5.0", + "path-to-regexp": "^2.2.1", + "querystring": "^0.2.0", + "whatwg-url": "^6.5.0" + }, + "engines": { + "node": ">=4.0.0" + }, + "funding": { + "type": "charity", + "url": "https://www.justgiving.com/refugee-support-europe" + }, + "peerDependencies": { + "node-fetch": "*" + }, + "peerDependenciesMeta": { + "node-fetch": { + "optional": true + } + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "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", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/from2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/from2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/from2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/from2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "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" + } + }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "5.0.2", + "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", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^4.0.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/gauge/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/gauge/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/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" + } + }, + "node_modules/gauge/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/gauge/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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-dep-tree": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-dep-tree/-/get-dep-tree-2.0.0.tgz", + "integrity": "sha512-kEPR3I4I3mFvywlHd8UcCaZRjHTfMctp532faCk1afUQL7XGaTep/YSZ15E64/iS9oVzgbJAWO5OzLRZTnfYhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@npmcli/arborist": "^6.5.1", + "array.prototype.flat": "^1.3.2", + "colors": "=1.4.0", + "lockfile-info": "^1.0.0", + "pacote": "^15.2.0" + }, + "engines": { + "node": ">= 19 || ^18 || ^16.13 || ^14.18" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-json": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-json/-/get-json-1.1.0.tgz", + "integrity": "sha512-IjoEwpXyyEsRtwBSZ0SYA6By6oVBnakpftFHAAkSSlLYRZ1NPGFS/r+6fSgbk7t6njfEuYVMbD1pe4ex6vgLcw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "jsonp": "^0.2.1", + "safe-buffer": "^5.2.1", + "safe-regex-test": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/glob": { + "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", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "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" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "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": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/humanize-ms": { + "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" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-2.1.0.tgz", + "integrity": "sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10 <11 || >=12 <13 || >=14" + } + }, + "node_modules/ignore-walk": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from-esm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-2.0.0.tgz", + "integrity": "sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=18.20" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/imurmurhash": { + "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" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/index-to-position": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.1.0.tgz", + "integrity": "sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/infer-owner": { + "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": { + "version": "1.0.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", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/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/inquirer/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/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/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/inquirer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/inquirer/node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/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/inquirer/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/inquirer/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/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ip-address": { + "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", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/irregular-plurals": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.5.0.tgz", + "integrity": "sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/is/-/is-3.3.0.tgz", + "integrity": "sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "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": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-subset": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz", + "integrity": "sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/iterate-iterator": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-iterator/-/iterate-iterator-1.0.2.tgz", + "integrity": "sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/iterate-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/iterate-value/-/iterate-value-1.0.2.tgz", + "integrity": "sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-get-iterator": "^1.0.2", + "iterate-iterator": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jackspeak": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/java-properties": { + "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" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-file-plus": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json-file-plus/-/json-file-plus-3.3.2.tgz", + "integrity": "sha512-KusE5Yl1vm5gjkHdPKEe2JcFO8u6DGRou9EW02sI3a0CjzvKwplTATPL59fHSbGw2eZR6mxU+LIpGGo881e+Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is": "^3.3.0", + "node.extend": "^2.0.3", + "object.assign": "^4.1.7", + "promiseback": "^2.0.3", + "safer-buffer": "^2.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "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" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-nice": { + "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" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", + "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", + "dev": true, + "dependencies": { + "debug": "^2.1.3" + } + }, + "node_modules/jsonp/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/jsonp/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonparse": { + "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" + ], + "license": "MIT" + }, + "node_modules/junk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", + "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/just-diff": { + "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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-7.0.1.tgz", + "integrity": "sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lockfile-info": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/lockfile-info/-/lockfile-info-1.1.0.tgz", + "integrity": "sha512-JG0/EwBh6AmfakC+fbEV+5gPUVZVB1kmdnbcJ5wllEbiuKc7ULcXyGi6o0q4JHQjKIg6YM3x82VZ4hoFA6tzkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2" + }, + "engines": { + "node": ">= 8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/lockfile-lint": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/lockfile-lint/-/lockfile-lint-4.14.1.tgz", + "integrity": "sha512-NW0Tk1qfldhbhJWQENYQWANdmlanXKxvTJYRYKn56INYjaP2M07Ua2SJYkUMS+ZbYwxDzul/C6pDsV/NEXrl+A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "cosmiconfig": "^9.0.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "lockfile-lint-api": "^5.9.2", + "yargs": "^17.7.2" + }, + "bin": { + "lockfile-lint": "bin/lockfile-lint.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/lockfile-lint-api": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/lockfile-lint-api/-/lockfile-lint-api-5.9.2.tgz", + "integrity": "sha512-3QhxWxl3jT9GcMxuCnTsU8Tz5U6U1lKBlKBu2zOYOz/x3ONUoojEtky3uzoaaDgExcLqIX0Aqv2I7TZXE383CQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@yarnpkg/parsers": "^3.0.0-rc.48.1", + "debug": "^4.3.4", + "object-hash": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/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/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ls-engines": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/ls-engines/-/ls-engines-0.9.3.tgz", + "integrity": "sha512-vvaAldgMQpU646VlFmHq73f723UycwZW7FeJCeP1GLuAGvjX/K336JIFPvz8Eo0fUXcyLIgbpD4s7HY9C7yb/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@npmcli/arborist": "^6.5.1", + "array.prototype.some": "^1.1.6", + "array.prototype.tosorted": "^1.1.4", + "colors": "=1.4.0", + "fast_array_intersect": "^1.1.0", + "get-dep-tree": "^2.0.0", + "get-json": "^1.1.0", + "json-file-plus": "^3.3.1", + "lockfile-info": "^1.0.0", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "pacote": "^15.2.0", + "promise.allsettled": "^1.0.7", + "semver": "^7.6.3", + "table": "^6.8.2", + "yargs": "^17.7.2" + }, + "bin": { + "ls-engines": "bin/ls-engines" + }, + "engines": { + "node": ">= 19 || ^18 || ^16.13 || ^14.18", + "npm": ">=8" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "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", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "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", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/aggregate-error": { + "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", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/cacache": { + "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", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/chownr": { + "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" + } + }, + "node_modules/make-fetch-happen/node_modules/clean-stack": { + "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" + } + }, + "node_modules/make-fetch-happen/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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/make-fetch-happen/node_modules/glob": { + "version": "8.1.0", + "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", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/make-fetch-happen/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", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "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", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/indent-string": { + "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" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "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" + } + }, + "node_modules/make-fetch-happen/node_modules/minimatch": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/p-map": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "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" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/tar": { + "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", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/tar/node_modules/minipass": { + "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" + } + }, + "node_modules/make-fetch-happen/node_modules/unique-filename": { + "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" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/unique-slug": { + "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" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "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/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", + "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "ansi-regex": "^6.1.0", + "chalk": "^5.4.1", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.2.0", + "supports-hyperlinks": "^3.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <16" + } + }, + "node_modules/marked-terminal/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/matcher": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-5.0.0.tgz", + "integrity": "sha512-s2EMBOWtXFc8dgqvoAzKJXxNHibcdJMV0gwqKUaw9E2JBJuGUK7DrNKrA6g/i+v72TT16+6sVm5mS3thaMLQUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "license": "MIT", + "dependencies": { + "blueimp-md5": "^2.10.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/memoize": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.1.0.tgz", + "integrity": "sha512-MMbFhJzh4Jlg/poq1si90XRlTZRDHVqdlz2mPyGJ6kqMpyHUyVpDd5gpFAvVehW64+RA1eKE9Yt8aSLY7w2Kgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "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": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.7.tgz", + "integrity": "sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==", + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "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" + } + }, + "node_modules/minipass-collect": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "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/minipass-fetch": { + "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", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch/node_modules/yallist": { + "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/minipass-flush": { + "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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "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/minipass-json-stream": { + "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", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream/node_modules/yallist": { + "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/minipass-pipeline": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "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/minipass-sized": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "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/minizlib": { + "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", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "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/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/negotiator": { + "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" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", + "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-gyp": { + "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", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/abbrev": { + "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" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "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", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "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", + "concat-map": "0.0.1" + } + }, + "node_modules/node-gyp/node_modules/chownr": { + "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" + } + }, + "node_modules/node-gyp/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/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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/fs-minipass/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "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", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "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", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/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/node-gyp/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-gyp/node_modules/minipass": { + "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" + } + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "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" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "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", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/signal-exit": { + "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", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/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/node-gyp/node_modules/tar": { + "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", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "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/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node.extend": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-2.0.3.tgz", + "integrity": "sha512-xwADg/okH48PvBmRZyoX8i8GJaKuJ1CqlqotlZOhUio8egD1P5trJupHKBzcPjSF9ifK2gPcEICRBnkfPqQXZw==", + "dev": true, + "license": "(MIT OR GPL-2.0)", + "dependencies": { + "hasown": "^2.0.0", + "is": "^3.3.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-package-data": { + "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", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz", + "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.9.2.tgz", + "integrity": "sha512-iriPEPIkoMYUy3F6f3wwSZAU93E0Eg6cHwIR6jzzOXWSy+SD/rOODEs74cVONHKSx2obXtuUoyidVEhISrisgQ==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^8.0.0", + "@npmcli/config": "^9.0.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/map-workspaces": "^4.0.2", + "@npmcli/package-json": "^6.1.0", + "@npmcli/promise-spawn": "^8.0.2", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "@sigstore/tuf": "^3.0.0", + "abbrev": "^3.0.0", + "archy": "~1.0.0", + "cacache": "^19.0.1", + "chalk": "^5.3.0", + "ci-info": "^4.1.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.5", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^8.0.2", + "ini": "^5.0.0", + "init-package-json": "^7.0.2", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^4.0.0", + "libnpmaccess": "^9.0.0", + "libnpmdiff": "^7.0.0", + "libnpmexec": "^9.0.0", + "libnpmfund": "^6.0.0", + "libnpmhook": "^11.0.0", + "libnpmorg": "^7.0.0", + "libnpmpack": "^8.0.0", + "libnpmpublish": "^10.0.1", + "libnpmsearch": "^8.0.0", + "libnpmteam": "^7.0.0", + "libnpmversion": "^7.0.0", + "make-fetch-happen": "^14.0.3", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^11.0.0", + "nopt": "^8.0.0", + "normalize-package-data": "^7.0.0", + "npm-audit-report": "^6.0.0", + "npm-install-checks": "^7.1.1", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-profile": "^11.0.1", + "npm-registry-fetch": "^18.0.2", + "npm-user-validate": "^3.0.0", + "p-map": "^4.0.0", + "pacote": "^19.0.1", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^4.0.0", + "semver": "^7.6.3", + "spdx-expression-parse": "^4.0.0", + "ssri": "^12.0.0", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^6.0.0", + "which": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-bundled": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "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" + } + }, + "node_modules/npm-package-arg": { + "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", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "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", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "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", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm-registry-fetch/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", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/https-proxy-agent": { + "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", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "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" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "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", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass": { + "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" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "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", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch/node_modules/minipass": { + "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" + } + }, + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" + } + }, + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm-run-all2/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2/node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^4.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/metavuln-calculator": "^8.0.0", + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.1", + "@npmcli/query": "^4.0.0", + "@npmcli/redact": "^3.0.0", + "@npmcli/run-script": "^9.0.1", + "bin-links": "^5.0.0", + "cacache": "^19.0.1", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^8.0.0", + "npm-install-checks": "^7.1.0", + "npm-package-arg": "^12.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.1", + "pacote": "^19.0.0", + "parse-conflict-json": "^4.0.0", + "proc-log": "^5.0.0", + "proggy": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "ssri": "^12.0.0", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^4.0.1", + "@npmcli/package-json": "^6.0.1", + "ci-info": "^4.0.0", + "ini": "^5.0.0", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^19.0.0", + "json-parse-even-better-errors": "^4.0.0", + "pacote": "^20.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "20.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.1.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "9.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^7.0.0", + "npm-normalize-package-bin": "^4.0.0", + "proc-log": "^5.0.0", + "read-cmd-shim": "^5.0.0", + "write-file-atomic": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/p-map": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.1.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^6.0.0", + "npm-package-arg": "^12.0.0", + "promzard": "^2.0.0", + "read": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.3", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "tar": "^6.2.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "ci-info": "^4.0.0", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0", + "proc-log": "^5.0.0", + "read": "^4.0.0", + "read-package-json-fast": "^4.0.0", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^8.0.0", + "@npmcli/run-script": "^9.0.1", + "npm-package-arg": "^12.0.0", + "pacote": "^19.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "10.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^7.0.0", + "npm-package-arg": "^12.0.0", + "npm-registry-fetch": "^18.0.1", + "proc-log": "^5.0.0", + "semver": "^7.3.7", + "sigstore": "^3.0.0", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^18.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.1", + "@npmcli/run-script": "^9.0.1", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.4.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "14.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/make-fetch-happen/node_modules/negotiator": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^8.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "9.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "11.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "18.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch/node_modules/minizlib": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.4", + "rimraf": "^5.0.5" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "19.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "5.0.10", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/bundle": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/core": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/sign": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/sigstore/node_modules/@sigstore/verify": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.20", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "12.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": 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/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": 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/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/tuf-js/node_modules/@tufjs/models": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": 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/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npmlog": { + "version": "7.0.1", + "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", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "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" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/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/ora/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/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/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/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-event": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-6.0.1.tgz", + "integrity": "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "license": "MIT", + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/package-config": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-config/-/package-config-5.0.0.tgz", + "integrity": "sha512-GYTTew2slBcYdvRHqjhwaaydVMvn/qrGC323+nKclYioNSLTDUM/lGgtGTgyHVtYcozb+XkE8CNhwcraOmZ9Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "load-json-file": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "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": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pacote": { + "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", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^5.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.3.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pacote/node_modules/chownr": { + "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" + } + }, + "node_modules/pacote/node_modules/minipass": { + "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" + } + }, + "node_modules/pacote/node_modules/mkdirp": { + "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" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pacote/node_modules/tar": { + "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", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pacote/node_modules/tar/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" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/pacote/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pacote/node_modules/yallist": { + "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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-conflict-json": { + "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", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "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" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "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", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "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-to-regexp": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.4.0.tgz", + "integrity": "sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/plur": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/plur/-/plur-5.1.0.tgz", + "integrity": "sha512-VP/72JeXqak2KiOzjgKtQen5y3IZHn+9GOuLDafPv0eXa47xq0At93XahYBs26MsifCQ4enGKwbjBTKgb9QJXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "irregular-plurals": "^3.3.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss-selector-parser": { + "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", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proc-log": { + "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" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-all-reject-late": { + "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" + } + }, + "node_modules/promise-call-limit": { + "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" + } + }, + "node_modules/promise-deferred": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/promise-deferred/-/promise-deferred-2.0.4.tgz", + "integrity": "sha512-clITUrRNue0lRawJPyHwCtgcPryJBtGXN6map89kM268+bKgSfh/1ZXOD51+VfDsihzF66m2PBmNOIHETfko4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "promise": "^8.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/promise-inflight": { + "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", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/promise.allsettled": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/promise.allsettled/-/promise.allsettled-1.0.7.tgz", + "integrity": "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.map": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "iterate-value": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/promiseback": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/promiseback/-/promiseback-2.0.3.tgz", + "integrity": "sha512-VZXdCwS0ppVNTIRfNsCvVwJAaP2b+pxQF7lM8DMWfmpNWyTxB6O5YNbzs+8z0ki/KIBHKHk308NTIl4kJUem3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.5", + "promise-deferred": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/publint": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/publint/-/publint-0.3.12.tgz", + "integrity": "sha512-1w3MMtL9iotBjm1mmXtG3Nk06wnq9UhGNRpQ2j6n1Zq7YAD6gnxMMZMIxlRPAydVjVbjSm+n0lhwqsD1m4LD5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@publint/pack": "^0.1.2", + "package-manager-detector": "^1.1.0", + "picocolors": "^1.1.1", + "sade": "^1.8.1" + }, + "bin": { + "publint": "src/cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://bjornlu.com/sponsor" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystring": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz", + "integrity": "sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==", + "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-cmd-shim": { + "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" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "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", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "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", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/read-pkg/node_modules/lru-cache": { + "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/read-pkg/node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "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", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/registry-auth-token": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", + "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "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/retry": { + "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" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "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" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "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", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "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", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/semantic-release": { + "version": "24.2.5", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.5.tgz", + "integrity": "sha512-9xV49HNY8C0/WmPWxTlaNleiXhWb//qfMzG2c5X8/k7tuWcu8RssbuS+sujb/h7PiWSXv53mrQvV9hrO9b7vuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^11.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^8.0.0", + "import-from-esm": "^2.0.0", + "lodash-es": "^4.17.21", + "marked": "^15.0.0", + "marked-terminal": "^7.3.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" + } + }, + "node_modules/semantic-release/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/semantic-release/node_modules/lru-cache": { + "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/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "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": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sigstore": { + "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", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/sigstore/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", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sigstore/node_modules/https-proxy-agent": { + "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", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sigstore/node_modules/lru-cache": { + "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" + } + }, + "node_modules/sigstore/node_modules/make-fetch-happen": { + "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", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/minipass": { + "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" + } + }, + "node_modules/sigstore/node_modules/minipass-fetch": { + "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", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/sigstore/node_modules/minipass-fetch/node_modules/minipass": { + "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" + } + }, + "node_modules/sinon": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "supports-color": "^7.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "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", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "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", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "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", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/sprintf-js": { + "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/ssri": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/stream-combiner2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-combiner2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/stream-combiner2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-combiner2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder": { + "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" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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/string-width-cjs/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/string-width-cjs/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/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" + } + }, + "node_modules/string-width-cjs/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/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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/strip-ansi-cjs/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/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supertap": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-3.0.1.tgz", + "integrity": "sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "js-yaml": "^3.14.1", + "serialize-error": "^7.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/supertap/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/supertap/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/supertap/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/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/table/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/table/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/table/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/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/table/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/table/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/tar": { + "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", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/minizlib": { + "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" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/tar/node_modules/yallist": { + "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" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/treeverse": { + "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" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tuf-js": { + "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", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/agent-base": { + "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" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/tuf-js/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", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tuf-js/node_modules/https-proxy-agent": { + "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", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tuf-js/node_modules/lru-cache": { + "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" + } + }, + "node_modules/tuf-js/node_modules/make-fetch-happen": { + "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", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/minipass": { + "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" + } + }, + "node_modules/tuf-js/node_modules/minipass-fetch": { + "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", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/tuf-js/node_modules/minipass-fetch/node_modules/minipass": { + "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" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "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" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "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" + } + }, + "node_modules/walk-up-path": { + "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/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "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" + } + }, + "node_modules/wide-align/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/wide-align/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/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" + } + }, + "node_modules/wide-align/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/wide-align/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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "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/wrap-ansi-cjs": { + "name": "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/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" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrappy": { + "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": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz", + "integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "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/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 053a4b87..47eec718 100644 --- a/package.json +++ b/package.json @@ -2,10 +2,15 @@ "name": "@semantic-release/github", "description": "semantic-release plugin to publish a GitHub release and comment on released Pull Requests/Issues", "version": "0.0.0-development", + "type": "module", "author": "Pierre Vanduynslager (https://twitter.com/@pvdlg_)", "ava": { - "helpers": [ - "test/helpers/**/*" + "files": [ + "test/**/*.test.js", + "!test/integration.test.js" + ], + "nodeArguments": [ + "--no-warnings" ] }, "bugs": { @@ -13,48 +18,50 @@ }, "contributors": [ "Stephan Bönnemann (http://boennemann.me)", - "Gregor Martynus (https://twitter.com/gr2m)" + "Gregor Martynus (https://twitter.com/gr2m)", + "Matt Travi (https://matt.travi.org/)" ], "dependencies": { - "@octokit/rest": "^16.27.0", - "@semantic-release/error": "^2.2.0", - "aggregate-error": "^3.0.0", - "bottleneck": "^2.18.1", - "debug": "^4.0.0", - "dir-glob": "^3.0.0", - "fs-extra": "^8.0.0", - "globby": "^10.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^3.0.0", - "issue-parser": "^5.0.0", - "lodash": "^4.17.4", - "mime": "^2.4.3", - "p-filter": "^2.0.0", - "p-retry": "^4.0.0", - "url-join": "^4.0.0" + "@octokit/core": "^7.0.0", + "@octokit/plugin-paginate-rest": "^13.0.0", + "@octokit/plugin-retry": "^8.0.0", + "@octokit/plugin-throttling": "^11.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" }, "devDependencies": { - "ava": "^2.0.0", - "clear-module": "^4.0.0", - "codecov": "^3.5.0", - "nock": "^11.1.0", - "nyc": "^14.1.1", - "proxy": "^1.0.0", - "proxyquire": "^2.0.0", - "semantic-release": "^15.13.12", - "server-destroy": "^1.0.1", - "sinon": "^7.1.1", - "tempy": "^0.3.0", - "xo": "^0.25.2" + "ava": "6.4.0", + "c8": "10.1.3", + "cpy": "11.1.0", + "cz-conventional-changelog": "3.3.0", + "fetch-mock": "npm:@gr2m/fetch-mock@9.11.0-pull-request-644.1", + "lockfile-lint": "4.14.1", + "ls-engines": "0.9.3", + "npm-run-all2": "8.0.4", + "prettier": "3.5.3", + "publint": "0.3.12", + "semantic-release": "24.2.5", + "sinon": "21.0.0", + "tempy": "3.1.0" }, "engines": { - "node": ">=8.3" + "node": ">=20.8.1" }, "files": [ "lib", "index.js" ], - "homepage": "https://github.com/semantic-release/github#readme", + "homepage": "https://github.com/semantic-release/github", "keywords": [ "git", "github", @@ -67,8 +74,8 @@ "version" ], "license": "MIT", - "main": "index.js", - "nyc": { + "exports": "./index.js", + "c8": { "include": [ "lib/**/*.js", "index.js" @@ -80,16 +87,25 @@ ], "all": true }, - "peerDependencies": { - "semantic-release": ">=15.8.0 <16.0.0" + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } }, - "prettier": { - "printWidth": 120, - "trailingComma": "es5" + "lockfile-lint": { + "path": "package-lock.json", + "type": "npm", + "validate-https": true, + "allowed-hosts": [ + "npm" + ] + }, + "peerDependencies": { + "semantic-release": ">=24.1.0" }, "publishConfig": { "access": "public", - "tag": "next" + "provenance": true }, "repository": { "type": "git", @@ -97,21 +113,19 @@ }, "scripts": { "codecov": "codecov -f coverage/coverage-final.json", - "lint": "xo", - "pretest": "npm run lint", - "semantic-release": "semantic-release", - "test": "nyc ava -v" + "lint:prettier": "prettier --check \"{lib,test}/**/*.{js,json,ts}\" \"*.{js,md,json}\" \".github/**/*.yml\"", + "lint:prettier:fix": "prettier --write \"{lib,test}/**/*.{js,json,ts}\" \"*.{js,md,json}\" \".github/**/*.yml\"", + "lint:lockfile": "lockfile-lint", + "lint:engines": "ls-engines", + "lint:publish": "publint --strict", + "test": "npm-run-all --print-label --parallel lint:* --parallel test:*", + "test:unit": "c8 ava --verbose", + "test:integration": "ava --verbose test/integration.test.js" }, - "xo": { - "prettier": true, - "space": true, - "rules": { - "camelcase": [ - "error", - { - "properties": "never" - } - ] - } - } + "renovate": { + "extends": [ + "github>semantic-release/.github:renovate-config" + ] + }, + "packageManager": "npm@11.4.1" } diff --git a/test/add-channel.test.js b/test/add-channel.test.js new file mode 100644 index 00000000..e14d696d --- /dev/null +++ b/test/add-channel.test.js @@ -0,0 +1,481 @@ +import test from "ava"; +import sinon from "sinon"; +import fetchMock from "fetch-mock"; + +import { TestOctokit } from "./helpers/test-octokit.js"; + +/* eslint camelcase: ["error", {properties: "never"}] */ + +import addChannel from "../lib/add-channel.js"; + +test.beforeEach((t) => { + // Mock logger + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; +}); + +test("Update a release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { + id: releaseId, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); + + const result = await addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Update a maintenance release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + channel: "1.x", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { + id: releaseId, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); + + const result = await addChannel( + pluginConfig, + { + env, + options, + branch: { type: "maintenance", channel: "1.x", main: false }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Update a prerelease", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { + id: releaseId, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); + + const result = await addChannel( + pluginConfig, + { + env, + options, + branch: { type: "maintenance", channel: "1.x", main: false }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Update a release with a custom github url", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GH_URL: "https://othertesturl.com:443", + GH_TOKEN: "github_token", + GH_PREFIX: "prefix", + }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `${env.GH_URL}/${owner}/${repo}.git` }; + const releaseUrl = `${env.GH_URL}/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://othertesturl.com:443/prefix/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { + id: releaseId, + }, + ) + .patchOnce( + `https://othertesturl.com:443/prefix/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); + + const result = await addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Create the new release if current one is missing", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + 404, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + }, + }, + ); + + const result = await addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "There is no release for tag %s, creating a new one", + nextRelease.gitTag, + ]); + t.deepEqual(t.context.log.args[1], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Throw error if cannot read current release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + 500, + ); + + const error = await t.throwsAsync( + addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(error.status, 500); + t.true(fetch.done()); +}); + +test("Throw error if cannot create missing current release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + 404, + ) + .postOnce(`https://api.github.local/repos/${owner}/${repo}/releases`, 500, { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + }, + }); + + const error = await t.throwsAsync( + addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(error.status, 500); + t.true(fetch.done()); +}); + +test("Throw error if cannot update release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { id: releaseId }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + 404, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); + + const error = await t.throwsAsync( + addChannel( + pluginConfig, + { + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(error.status, 404); + t.true(fetch.done()); +}); diff --git a/test/fail.test.js b/test/fail.test.js index df2a5c79..6daa543d 100644 --- a/test/fail.test.js +++ b/test/fail.test.js @@ -1,287 +1,647 @@ -import {escape} from 'querystring'; -import test from 'ava'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import SemanticReleaseError from '@semantic-release/error'; -import ISSUE_ID from '../lib/definitions/sr-issue-id'; -import {authenticate} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; +import SemanticReleaseError from "@semantic-release/error"; +import sinon from "sinon"; +import test from "ava"; +import fetchMock from "fetch-mock"; + +import { ISSUE_ID, RELEASE_FAIL_LABEL } from "../lib/definitions/constants.js"; +import { TestOctokit } from "./helpers/test-octokit.js"; /* eslint camelcase: ["error", {properties: "never"}] */ -const fail = proxyquire('../lib/fail', { - './get-client': proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}), -}); +import fail from "../lib/fail.js"; -test.beforeEach(t => { +test.beforeEach((t) => { // Mock logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; -}); - -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; }); -test.serial('Open a new issue with the list of errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const redirectedOwner = 'test_user_2'; - const redirectedRepo = 'test_repo_2'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Open a new issue with the list of errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const redirectedOwner = "test_user_2"; + const redirectedRepo = "test_repo_2"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${redirectedOwner}/${redirectedRepo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${redirectedOwner}/${redirectedRepo}`)}+${escape( - 'type:issue' - )}+${escape('state:open')}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - labels: ['semantic-release'], + const fetch = fetchMock + .sandbox() + .getOnce("https://api.github.local/repos/test_user/test_repo", { + full_name: `${redirectedOwner}/${redirectedRepo}`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent( + `repo:${redirectedOwner}/${redirectedRepo}`, + )}+${encodeURIComponent("type:issue")}+${encodeURIComponent( + "state:open", + )}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + (url, { body }) => { + t.is( + url, + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues`, + ); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + const data = JSON.parse(body); + t.is(data.title, failTitle); + t.regex( + data.body, + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, + ); + t.deepEqual(data.labels, ["semantic-release", RELEASE_FAIL_LABEL]); + return true; + }, + { + html_url: "https://github.com/issues/1", + number: 1, + }, + ); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); -test.serial('Open a new issue with the list of errors, retrying 4 times', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Open a new issue with the list of errors and custom title and comment", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "Custom title"; + const failComment = `branch \${branch.name} \${errors[0].message} \${errors[1].message} \${errors[2].message}`; + const pluginConfig = { failTitle, failComment }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .times(3) - .reply(404) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - labels: ['semantic-release'], + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) - .times(3) - .reply(500) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - labels: ['semantic-release'], + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues`, + { html_url: "https://github.com/issues/1", number: 1 }, + { + body: { + title: failTitle, + body: `branch master Error message 1 Error message 2 Error message 3\n\n${ISSUE_ID}`, + labels: ["semantic-release", RELEASE_FAIL_LABEL], + }, + }, + ); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); -test.serial('Open a new issue with the list of errors and custom title and comment', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'Custom title'; - const failComment = `branch \${branch} \${errors[0].message} \${errors[1].message} \${errors[2].message}`; - const pluginConfig = {failTitle, failComment}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Open a new issue with assignees and the list of errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const assignees = ["user1", "user2"]; + const pluginConfig = { failTitle, assignees }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: `branch master Error message 1 Error message 2 Error message 3\n\n${ISSUE_ID}`, - labels: ['semantic-release'], + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, `https://api.github.local/repos/${owner}/${repo}/issues`); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + const data = JSON.parse(body); + t.is(data.title, failTitle); + t.regex( + data.body, + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/, + ); + t.deepEqual(data.labels, ["semantic-release", RELEASE_FAIL_LABEL]); + t.deepEqual(data.assignees, ["user1", "user2"]); + return true; + }, + { html_url: "https://github.com/issues/1", number: 1 }, + ); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); -test.serial('Open a new issue with assignees and the list of errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const assignees = ['user1', 'user2']; - const pluginConfig = {failTitle, assignees}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Open a new issue without labels and the list of errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const labels = false; + const pluginConfig = { failTitle, labels }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/, - labels: ['semantic-release'], - assignees: ['user1', 'user2'], + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, `https://api.github.local/repos/${owner}/${repo}/issues`); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + const data = JSON.parse(body); + t.is(data.title, failTitle); + t.regex( + data.body, + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/, + ); + t.deepEqual(data.labels, [RELEASE_FAIL_LABEL]); + return true; + }, + { html_url: "https://github.com/issues/1", number: 1 }, + ); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); -test.serial('Open a new issue without labels and the list of errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const labels = false; - const pluginConfig = {failTitle, labels}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Update the first existing issue with the list of errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---/, - labels: [], + const issues = [ + { number: 1, body: "Issue 1 body", title: failTitle }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }) + .postOnce( + (url, { body }) => { + t.is( + url, + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + ); + t.regex( + JSON.parse(body).body, + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, + ); + return true; + }, + { html_url: "https://github.com/issues/2", number: 2 }, + ); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + t.true( + t.context.log.calledWith("Found existing semantic-release issue #%d.", 2), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s.", + 2, + "https://github.com/issues/2", + ), + ); + t.true(fetch.done()); }); -test.serial('Update the first existing issue with the list of errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test('Skip if "failComment" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failComment: false }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), - ]; - const issues = [ - {number: 1, body: 'Issue 1 body', title: failTitle}, - {number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle}, - {number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle}, + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: issues}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, { - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - }) - .reply(200, {html_url: 'https://github.com/issues/2', number: 2}); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.true(t.context.log.calledWith('Found existing semantic-release issue #%d.', 2)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s.', 2, 'https://github.com/issues/2')); - t.true(github.isDone()); + t.true(t.context.log.calledWith("Skip issue creation.")); }); -test.serial('Skip if "failComment" is "false"', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const pluginConfig = {failComment: false}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test('Skip if "failTitle" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failTitle: false }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true(t.context.log.calledWith("Skip issue creation.")); +}); + +test('Does not post comments if "failCommentCondition" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failCommentCondition: false }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.true(t.context.log.calledWith('Skip issue creation.')); + t.true(t.context.log.calledWith("Skip issue creation.")); }); -test.serial('Skip if "failTitle" is "false"', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const pluginConfig = {failTitle: false}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test('Does not post comments on existing issues when "failCommentCondition" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const failTitle = "The automated release is failing 🚨"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failCommentCondition: "<% return false; %>" }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; + const issues = [ + { number: 1, body: "Issue 1 body", title: failTitle }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getSRIssues("), + { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }, + ); + + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true(fetch.done()); + t.true(t.context.log.calledWith("Skip commenting on or creating an issue.")); +}); + +test(`Post new issue if none exists yet, but don't comment on existing issues when "failCommentCondition" disallows it`, async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const errors = [{ message: "An error occured" }]; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { + failTitle, + failComment: `Error: Release for branch \${branch.name} failed with error: \${errors.map(error => error.message).join(';')}`, + failCommentCondition: "<% return !issue; %>", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getSRIssues("), + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, `https://api.github.local/repos/${owner}/${repo}/issues`); + t.regex( + JSON.parse(body).body, + /Error: Release for branch master failed with error: An error occured\n\n/, + ); + return true; + }, + { html_url: "https://github.com/issues/2", number: 2 }, + ); - await fail(pluginConfig, {env, options, errors, logger: t.context.logger}); + await fail( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.true(t.context.log.calledWith('Skip issue creation.')); + t.true(fetch.done()); + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 2, + "https://github.com/issues/2", + ), + ); }); diff --git a/test/find-sr-issue.test.js b/test/find-sr-issue.test.js index 063c99ad..3916e859 100644 --- a/test/find-sr-issue.test.js +++ b/test/find-sr-issue.test.js @@ -1,130 +1,216 @@ -import {escape} from 'querystring'; -import test from 'ava'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import ISSUE_ID from '../lib/definitions/sr-issue-id'; -import findSRIssues from '../lib/find-sr-issues'; -import {authenticate} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; - -const githubToken = 'github_token'; -const client = proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit})({githubToken}); - -test.beforeEach(t => { - // Mock logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; -}); +import sinon from "sinon"; +import test from "ava"; +import fetchMock from "fetch-mock"; + +import { ISSUE_ID } from "../lib/definitions/constants.js"; +import findSRIssues from "../lib/find-sr-issues.js"; +import { TestOctokit } from "./helpers/test-octokit.js"; -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); +test.beforeEach((t) => { + // Mock logger + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; }); -test.serial('Filter out issues without ID', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const githubToken = 'github_token'; - const title = 'The automated release is failing 🚨'; +test("Filter out issues without ID", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const title = "The automated release is failing 🚨"; + const labels = []; const issues = [ - {number: 1, body: 'Issue 1 body', title}, - {number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title}, - {number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title}, + { number: 1, body: "Issue 1 body", title }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title }, ]; - const github = authenticate({}, {githubToken}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(title)}` - ) - .reply(200, {items: issues}); - const srIssues = await findSRIssues(client, title, owner, repo); + const fetch = fetchMock + .sandbox() + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }); + + const srIssues = await findSRIssues( + new TestOctokit({ request: { fetch } }), + t.context.logger, + title, + labels, + owner, + repo, + ); t.deepEqual(srIssues, [ - {number: 2, body: 'Issue 2 body\n\n', title}, - {number: 3, body: 'Issue 3 body\n\n', title}, + { + number: 2, + body: "Issue 2 body\n\n", + title, + }, + { + number: 3, + body: "Issue 3 body\n\n", + title, + }, ]); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Return empty array if not issues found', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const githubToken = 'github_token'; - const title = 'The automated release is failing 🚨'; +test("Return empty array if not issues found", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const title = "The automated release is failing 🚨"; + const labels = []; const issues = []; - const github = authenticate({}, {githubToken}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(title)}` - ) - .reply(200, {items: issues}); - - const srIssues = await findSRIssues(client, title, owner, repo); + const fetch = fetchMock + .sandbox() + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(title)}`, + { items: issues }, + ); + + const srIssues = await findSRIssues( + new TestOctokit({ request: { fetch } }), + t.context.logger, + title, + labels, + owner, + repo, + ); t.deepEqual(srIssues, []); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Return empty array if not issues has matching ID', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const githubToken = 'github_token'; - const title = 'The automated release is failing 🚨'; - const issues = [{number: 1, body: 'Issue 1 body', title}, {number: 2, body: 'Issue 2 body', title}]; - const github = authenticate({}, {githubToken}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(title)}` - ) - .reply(200, {items: issues}); - - const srIssues = await findSRIssues(client, title, owner, repo); +test("Return empty array if not issues has matching ID", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const title = "The automated release is failing 🚨"; + const labels = []; + const issues = [ + { number: 1, body: "Issue 1 body", title }, + { number: 2, body: "Issue 2 body", title }, + ]; + const fetch = fetchMock + .sandbox() + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }); + + const srIssues = await findSRIssues( + new TestOctokit({ request: { fetch } }), + t.context.logger, + title, + labels, + owner, + repo, + ); t.deepEqual(srIssues, []); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Retries 4 times', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const title = 'The automated release is failing :rotating_light:'; - const github = authenticate({}, {githubToken}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(title)}` - ) - .times(4) - .reply(422); - - const error = await t.throwsAsync(findSRIssues(client, title, owner, repo)); - - t.is(error.status, 422); - t.true(github.isDone()); -}); +test("Handle error in searchAPI fallback", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const title = "The automated release is failing 🚨"; + const labels = []; + const issues = []; -test.serial('Do not retry on 401 error', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const title = 'The automated release is failing :rotating_light:'; - const github = authenticate({}, {githubToken}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(title)}` - ) - .reply(401); - - const error = await t.throwsAsync(findSRIssues(client, title, owner, repo)); - - t.is(error.status, 401); - t.true(github.isDone()); + const response = new Response("Not Found", { + url: "https://api.github.com/search/issues?q=in%3Atitle+repo%3Aourorg%2Frepo+type%3Aissue+state%3Aopen+The%20automated%20release%20is%20failing%20%F0%9F%9A%A8", + status: 403, + headers: { + "access-control-allow-origin": "*", + "access-control-expose-headers": + "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset", + "content-encoding": "gzip", + "content-security-policy": "default-repo 'none'", + "content-type": "application/json; charset=utf-8", + date: "Tue, 28 May 2024 19:49:00 GMT", + "referrer-policy": + "origin-when-cross-origin, strict-origin-when-cross-origin", + server: "GitHub.com", + "strict-transport-security": + "max-age=31536000; includeSubdomains; preload", + "transfer-encoding": "chunked", + vary: "Accept-Encoding, Accept, X-Requested-With", + "x-content-type-options": "nosniff", + "x-frame-options": "deny", + "x-github-api-version-selected": "2022-11-28", + "x-github-media-type": "github.v3; format=json", + "x-github-request-id": "2**0:29*****4:3868737:6*****3:6****52C", + "x-ratelimit-limit": "30", + "x-ratelimit-remaining": "30", + "x-ratelimit-reset": "1716925800", + "x-ratelimit-resource": "search", + "x-ratelimit-used": "1", + "x-xss-protection": "0", + }, + data: { + documentation_url: + "https://docs.github.com/free-pro-team@latest/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits", + message: + "You have exceeded a secondary rate limit. Please wait a few minutes before you try again. If you reach out to GitHub Support for help, please include the request ID 2840:295B44:3868737:64A2183:6232352C.", + }, + }); + + const fetch = fetchMock + .sandbox() + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(title)}`, + response, + ); + + const srIssues = await findSRIssues( + new TestOctokit({ request: { fetch } }), + t.context.logger, + title, + labels, + owner, + repo, + ); + + t.true( + t.context.log.calledWith( + "An error occured fetching issue via fallback (with GH SearchAPI)", + ), + ); + t.log(t.context.log); + t.true(fetch.done()); }); diff --git a/test/get-client.test.js b/test/get-client.test.js deleted file mode 100644 index dc046bb0..00000000 --- a/test/get-client.test.js +++ /dev/null @@ -1,200 +0,0 @@ -import path from 'path'; -import http from 'http'; -import https from 'https'; -import {promisify} from 'util'; -import {readFile} from 'fs-extra'; -import test from 'ava'; -import {inRange} from 'lodash'; -import {stub, spy} from 'sinon'; -import proxyquire from 'proxyquire'; -import Proxy from 'proxy'; -import serverDestroy from 'server-destroy'; -import Octokit from '@octokit/rest'; -import rateLimit from './helpers/rate-limit'; - -const getClient = proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}); - -test.serial('Use a http proxy', async t => { - const server = http.createServer(); - await promisify(server.listen).bind(server)(); - const serverPort = server.address().port; - serverDestroy(server); - const proxy = new Proxy(); - await promisify(proxy.listen).bind(proxy)(); - const proxyPort = proxy.address().port; - serverDestroy(proxy); - - const proxyHandler = spy(); - const serverHandler = spy((req, res) => { - res.end(); - }); - proxy.on('request', proxyHandler); - server.on('request', serverHandler); - - const github = getClient({ - githubToken: 'github_token', - githubUrl: `http://localhost:${serverPort}`, - githubApiPathPrefix: '', - proxy: `http://localhost:${proxyPort}`, - }); - - await github.repos.get({repo: 'repo', owner: 'owner'}); - - t.is(proxyHandler.args[0][0].headers.accept, 'application/vnd.github.v3+json'); - t.is(serverHandler.args[0][0].headers.accept, 'application/vnd.github.v3+json'); - t.regex(serverHandler.args[0][0].headers.via, /proxy/); - t.truthy(serverHandler.args[0][0].headers['x-forwarded-for']); - - await promisify(proxy.destroy).bind(proxy)(); - await promisify(server.destroy).bind(server)(); -}); - -test.serial('Use a https proxy', async t => { - const server = https.createServer({ - key: await readFile(path.join(__dirname, '/fixtures/ssl/ssl-cert-snakeoil.key')), - cert: await readFile(path.join(__dirname, '/fixtures/ssl/ssl-cert-snakeoil.pem')), - }); - await promisify(server.listen).bind(server)(); - const serverPort = server.address().port; - serverDestroy(server); - const proxy = new Proxy(); - await promisify(proxy.listen).bind(proxy)(); - const proxyPort = proxy.address().port; - serverDestroy(proxy); - - const proxyHandler = spy(); - const serverHandler = spy((req, res) => { - res.end(); - }); - proxy.on('connect', proxyHandler); - server.on('request', serverHandler); - - const github = getClient({ - githubToken: 'github_token', - githubUrl: `https://localhost:${serverPort}`, - githubApiPathPrefix: '', - proxy: {host: 'localhost', port: proxyPort, rejectUnauthorized: false, headers: {foo: 'bar'}}, - }); - - await github.repos.get({repo: 'repo', owner: 'owner'}); - - t.is(proxyHandler.args[0][0].url, `localhost:${serverPort}`); - t.is(proxyHandler.args[0][0].headers.foo, 'bar'); - t.is(serverHandler.args[0][0].headers.accept, 'application/vnd.github.v3+json'); - - await promisify(proxy.destroy).bind(proxy)(); - await promisify(server.destroy).bind(server)(); -}); - -test('Use the global throttler for all endpoints', async t => { - const rate = 150; - - const octokit = new Octokit(); - octokit.hook.wrap('request', () => Date.now()); - const github = proxyquire('../lib/get-client', { - '@octokit/rest': stub().returns(octokit), - './definitions/rate-limit': {RATE_LIMITS: {search: 1, core: 1}, GLOBAL_RATE_LIMIT: rate}, - })({githubToken: 'token'}); - - const a = await github.repos.createRelease(); - const b = await github.issues.createComment(); - const c = await github.repos.createRelease(); - const d = await github.issues.createComment(); - const e = await github.search.issuesAndPullRequests(); - const f = await github.search.issuesAndPullRequests(); - - // `issues.createComment` should be called `rate` ms after `repos.createRelease` - t.true(inRange(b - a, rate - 50, rate + 50)); - // `repos.createRelease` should be called `rate` ms after `issues.createComment` - t.true(inRange(c - b, rate - 50, rate + 50)); - // `issues.createComment` should be called `rate` ms after `repos.createRelease` - t.true(inRange(d - c, rate - 50, rate + 50)); - // `search.issuesAndPullRequests` should be called `rate` ms after `issues.createComment` - t.true(inRange(e - d, rate - 50, rate + 50)); - // `search.issuesAndPullRequests` should be called `rate` ms after `search.issuesAndPullRequests` - t.true(inRange(f - e, rate - 50, rate + 50)); -}); - -test('Use the same throttler for endpoints in the same rate limit group', async t => { - const searchRate = 300; - const coreRate = 150; - - const octokit = new Octokit(); - octokit.hook.wrap('request', () => Date.now()); - const github = proxyquire('../lib/get-client', { - '@octokit/rest': stub().returns(octokit), - './definitions/rate-limit': {RATE_LIMITS: {search: searchRate, core: coreRate}, GLOBAL_RATE_LIMIT: 1}, - })({githubToken: 'token'}); - - const a = await github.repos.createRelease(); - const b = await github.issues.createComment(); - const c = await github.repos.createRelease(); - const d = await github.issues.createComment(); - const e = await github.search.issuesAndPullRequests(); - const f = await github.search.issuesAndPullRequests(); - - // `issues.createComment` should be called `coreRate` ms after `repos.createRelease` - t.true(inRange(b - a, coreRate - 50, coreRate + 50)); - // `repos.createRelease` should be called `coreRate` ms after `issues.createComment` - t.true(inRange(c - b, coreRate - 50, coreRate + 50)); - // `issues.createComment` should be called `coreRate` ms after `repos.createRelease` - t.true(inRange(d - c, coreRate - 50, coreRate + 50)); - - // The first search should be called immediatly as it uses a different throttler - t.true(inRange(e - d, -50, 50)); - // The second search should be called only after `searchRate` ms - t.true(inRange(f - e, searchRate - 50, searchRate + 50)); -}); - -test('Use different throttler for read and write endpoints', async t => { - const writeRate = 300; - const readRate = 150; - - const octokit = new Octokit(); - octokit.hook.wrap('request', () => Date.now()); - const github = proxyquire('../lib/get-client', { - '@octokit/rest': stub().returns(octokit), - './definitions/rate-limit': {RATE_LIMITS: {core: {write: writeRate, read: readRate}}, GLOBAL_RATE_LIMIT: 1}, - })({githubToken: 'token'}); - - const a = await github.repos.get(); - const b = await github.repos.get(); - const c = await github.repos.createRelease(); - const d = await github.repos.createRelease(); - - // `repos.get` should be called `readRate` ms after `repos.get` - t.true(inRange(b - a, readRate - 50, readRate + 50)); - // `repos.createRelease` should be called `coreRate` ms after `repos.createRelease` - t.true(inRange(d - c, writeRate - 50, writeRate + 50)); -}); - -test('Use the same throttler when retrying', async t => { - const coreRate = 200; - const request = stub().callsFake(async () => { - const err = new Error(); - err.time = Date.now(); - err.status = 404; - throw err; - }); - const octokit = new Octokit(); - octokit.hook.wrap('request', request); - const github = proxyquire('../lib/get-client', { - '@octokit/rest': stub().returns(octokit), - './definitions/rate-limit': { - RETRY_CONF: {retries: 3, factor: 1, minTimeout: 1}, - RATE_LIMITS: {core: coreRate}, - GLOBAL_RATE_LIMIT: 1, - }, - })({githubToken: 'token'}); - - await t.throwsAsync(github.repos.createRelease()); - const {time: a} = await t.throwsAsync(request.getCall(0).returnValue); - const {time: b} = await t.throwsAsync(request.getCall(1).returnValue); - const {time: c} = await t.throwsAsync(request.getCall(2).returnValue); - const {time: d} = await t.throwsAsync(request.getCall(3).returnValue); - - // Each retry should be done after `coreRate` ms - t.true(inRange(b - a, coreRate - 50, coreRate + 50)); - t.true(inRange(c - b, coreRate - 50, coreRate + 50)); - t.true(inRange(d - c, coreRate - 50, coreRate + 50)); -}); diff --git a/test/get-fail-comment.test.js b/test/get-fail-comment.test.js index b7ebb92f..a7115235 100644 --- a/test/get-fail-comment.test.js +++ b/test/get-fail-comment.test.js @@ -1,51 +1,54 @@ -import test from 'ava'; -import SemanticReleaseError from '@semantic-release/error'; -import getfailComment from '../lib/get-fail-comment'; +import test from "ava"; +import SemanticReleaseError from "@semantic-release/error"; -test('Comment with mutiple errors', t => { +import getfailComment from "../lib/get-fail-comment.js"; + +test("Comment with mutiple errors", (t) => { const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const comment = getfailComment('master', errors); + const comment = getfailComment({ name: "master" }, errors); t.regex(comment, /the `master` branch/); t.regex( comment, - /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/ + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, ); }); -test('Comment with one error', t => { - const errors = [new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details')]; - const comment = getfailComment('master', errors); +test("Comment with one error", (t) => { + const errors = [ + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + ]; + const comment = getfailComment({ name: "master" }, errors); t.regex(comment, /the `master` branch/); t.regex(comment, /---\n\n### Error message 1\n\nError 1 details\n\n---/); }); -test('Comment with missing error details and pluginName', t => { - const error = new SemanticReleaseError('Error message 1', 'ERR1'); - error.pluginName = 'some-plugin'; +test("Comment with missing error details and pluginName", (t) => { + const error = new SemanticReleaseError("Error message 1", "ERR1"); + error.pluginName = "some-plugin"; const errors = [error]; - const comment = getfailComment('master', errors); + const comment = getfailComment({ name: "master" }, errors); t.regex(comment, /the `master` branch/); t.regex( comment, - /---\n\n### Error message 1\n\nUnfortunately this error doesn't have any additional information. Feel free to kindly ask the author of the `some-plugin` plugin to add more helpful information.\n\n---/ + /---\n\n### Error message 1\n\nUnfortunately this error doesn't have any additional information. Feel free to kindly ask the author of the `some-plugin` plugin to add more helpful information.\n\n---/, ); }); -test('Comment with missing error details and no pluginName', t => { - const error = new SemanticReleaseError('Error message 1', 'ERR1'); +test("Comment with missing error details and no pluginName", (t) => { + const error = new SemanticReleaseError("Error message 1", "ERR1"); const errors = [error]; - const comment = getfailComment('master', errors); + const comment = getfailComment({ name: "master" }, errors); t.regex(comment, /the `master` branch/); t.regex( comment, - /---\n\n### Error message 1\n\nUnfortunately this error doesn't have any additional information.\n\n---/ + /---\n\n### Error message 1\n\nUnfortunately this error doesn't have any additional information.\n\n---/, ); }); diff --git a/test/get-release-links.test.js b/test/get-release-links.test.js new file mode 100644 index 00000000..f1028f49 --- /dev/null +++ b/test/get-release-links.test.js @@ -0,0 +1,77 @@ +import test from "ava"; + +import getReleaseLinks from "../lib/get-release-links.js"; +import { RELEASE_NAME } from "../lib/definitions/constants.js"; + +test("Comment for release with multiple releases", (t) => { + const releaseInfos = [ + { name: RELEASE_NAME, url: "https://github.com/release" }, + { name: "Http release", url: "https://release.com/release" }, + { name: "npm release", url: "https://npm.com/release" }, + ]; + const comment = getReleaseLinks(releaseInfos); + + t.is( + comment, + `This release is also available on: +- [Http release](https://release.com/release) +- [npm release](https://npm.com/release)`, + ); +}); + +test("Release with missing release URL", (t) => { + const releaseInfos = [ + { name: RELEASE_NAME, url: "https://github.com/release" }, + { name: "Http release", url: "https://release.com/release" }, + { name: "npm release" }, + ]; + const comment = getReleaseLinks(releaseInfos); + + t.is( + comment, + `This release is also available on: +- [Http release](https://release.com/release) +- \`npm release\``, + ); +}); + +test("Release with one release", (t) => { + const releaseInfos = [ + { name: RELEASE_NAME, url: "https://github.com/release" }, + { name: "Http release", url: "https://release.com/release" }, + ]; + const comment = getReleaseLinks(releaseInfos); + + t.is( + comment, + `This release is also available on: +- [Http release](https://release.com/release)`, + ); +}); + +test("Release with non http releases", (t) => { + const releaseInfos = [{ name: "S3", url: "s3://my-bucket/release-asset" }]; + const comment = getReleaseLinks(releaseInfos); + + t.is( + comment, + `This release is also available on: +- S3: \`s3://my-bucket/release-asset\``, + ); +}); + +test("Release with only github release", (t) => { + const releaseInfos = [ + { name: RELEASE_NAME, url: "https://github.com/release" }, + ]; + const comment = getReleaseLinks(releaseInfos); + + t.is(comment, ""); +}); + +test("Comment with no release object", (t) => { + const releaseInfos = []; + const comment = getReleaseLinks(releaseInfos); + + t.is(comment, ""); +}); diff --git a/test/get-search-queries.test.js b/test/get-search-queries.test.js deleted file mode 100644 index 97cf2ee0..00000000 --- a/test/get-search-queries.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import test from 'ava'; -import {repeat} from 'lodash'; -import getSearchQueries from '../lib/get-search-queries'; - -test('Generate queries of 256 characters maximum', t => { - const commits = [ - repeat('a', 40), - repeat('b', 40), - repeat('c', 40), - repeat('d', 40), - repeat('e', 40), - repeat('f', 40), - ]; - - t.deepEqual(getSearchQueries(repeat('0', 51), commits), [ - `${repeat('0', 51)}+${commits[0]}+${commits[1]}+${commits[2]}+${commits[3]}+${commits[4]}`, - `${repeat('0', 51)}+${commits[5]}`, - ]); - - t.deepEqual(getSearchQueries(repeat('0', 52), commits), [ - `${repeat('0', 52)}+${commits[0]}+${commits[1]}+${commits[2]}+${commits[3]}`, - `${repeat('0', 52)}+${commits[4]}+${commits[5]}`, - ]); -}); - -test('Generate one query if it is less tahn 256 characters', t => { - const commits = [repeat('a', 40), repeat('b', 40)]; - - t.deepEqual(getSearchQueries(repeat('0', 20), commits), [`${repeat('0', 20)}+${commits[0]}+${commits[1]}`]); -}); - -test('Return emty Array if there is no commits', t => { - t.deepEqual(getSearchQueries('base', []), []); -}); diff --git a/test/get-success-comment.test.js b/test/get-success-comment.test.js index 8df43601..63281c60 100644 --- a/test/get-success-comment.test.js +++ b/test/get-success-comment.test.js @@ -1,15 +1,16 @@ -import test from 'ava'; -import getSuccessComment from '../lib/get-success-comment'; +import test from "ava"; -const HOME_URL = 'https://github.com/semantic-release/semantic-release'; +import getSuccessComment from "../lib/get-success-comment.js"; -test('Comment for issue with multiple releases', t => { - const issue = {number: 1}; +const HOME_URL = "https://github.com/semantic-release/semantic-release"; + +test("Comment for issue with multiple releases", (t) => { + const issue = { number: 1 }; const releaseInfos = [ - {name: 'GitHub release', url: 'https://github.com/release'}, - {name: 'npm release', url: 'https://npm.com/release'}, + { name: "GitHub release", url: "https://github.com/release" }, + { name: "npm release", url: "https://npm.com/release" }, ]; - const nextRelease = {version: '1.0.0'}; + const nextRelease = { version: "1.0.0" }; const comment = getSuccessComment(issue, releaseInfos, nextRelease); t.is( @@ -20,17 +21,17 @@ The release is available on: - [GitHub release](https://github.com/release) - [npm release](https://npm.com/release) -Your **[semantic-release](${HOME_URL})** bot :package::rocket:` +Your **[semantic-release](${HOME_URL})** bot :package::rocket:`, ); }); -test('Comment for PR with multiple releases', t => { - const issue = {number: 1, pull_request: {}}; +test("Comment for PR with multiple releases", (t) => { + const issue = { number: 1, pull_request: {} }; const releaseInfos = [ - {name: 'GitHub release', url: 'https://github.com/release'}, - {name: 'npm release', url: 'https://npm.com/release'}, + { name: "GitHub release", url: "https://github.com/release" }, + { name: "npm release", url: "https://npm.com/release" }, ]; - const nextRelease = {version: '1.0.0'}; + const nextRelease = { version: "1.0.0" }; const comment = getSuccessComment(issue, releaseInfos, nextRelease); t.is( @@ -41,14 +42,17 @@ The release is available on: - [GitHub release](https://github.com/release) - [npm release](https://npm.com/release) -Your **[semantic-release](${HOME_URL})** bot :package::rocket:` +Your **[semantic-release](${HOME_URL})** bot :package::rocket:`, ); }); -test('Comment with missing release URL', t => { - const issue = {number: 1}; - const releaseInfos = [{name: 'GitHub release', url: 'https://github.com/release'}, {name: 'npm release'}]; - const nextRelease = {version: '1.0.0'}; +test("Comment with missing release URL", (t) => { + const issue = { number: 1 }; + const releaseInfos = [ + { name: "GitHub release", url: "https://github.com/release" }, + { name: "npm release" }, + ]; + const nextRelease = { version: "1.0.0" }; const comment = getSuccessComment(issue, releaseInfos, nextRelease); t.is( @@ -59,14 +63,16 @@ The release is available on: - [GitHub release](https://github.com/release) - \`npm release\` -Your **[semantic-release](${HOME_URL})** bot :package::rocket:` +Your **[semantic-release](${HOME_URL})** bot :package::rocket:`, ); }); -test('Comment with one release', t => { - const issue = {number: 1}; - const releaseInfos = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const nextRelease = {version: '1.0.0'}; +test("Comment with one release", (t) => { + const issue = { number: 1 }; + const releaseInfos = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + const nextRelease = { version: "1.0.0" }; const comment = getSuccessComment(issue, releaseInfos, nextRelease); t.is( @@ -75,20 +81,20 @@ test('Comment with one release', t => { The release is available on [GitHub release](https://github.com/release) -Your **[semantic-release](${HOME_URL})** bot :package::rocket:` +Your **[semantic-release](${HOME_URL})** bot :package::rocket:`, ); }); -test('Comment with no release object', t => { - const issue = {number: 1}; +test("Comment with no release object", (t) => { + const issue = { number: 1 }; const releaseInfos = []; - const nextRelease = {version: '1.0.0'}; + const nextRelease = { version: "1.0.0" }; const comment = getSuccessComment(issue, releaseInfos, nextRelease); t.is( comment, `:tada: This issue has been resolved in version 1.0.0 :tada: -Your **[semantic-release](${HOME_URL})** bot :package::rocket:` +Your **[semantic-release](${HOME_URL})** bot :package::rocket:`, ); }); diff --git a/test/glob-assets.test.js b/test/glob-assets.test.js index 4aea809b..1cea2ad6 100644 --- a/test/glob-assets.test.js +++ b/test/glob-assets.test.js @@ -1,193 +1,258 @@ -import path from 'path'; -import test from 'ava'; -import {copy, ensureDir} from 'fs-extra'; -import {isPlainObject, sortBy} from 'lodash'; -import tempy from 'tempy'; -import globAssets from '../lib/glob-assets'; +import { resolve } from "node:path"; +import { mkdir } from "node:fs/promises"; -const sortAssets = assets => sortBy(assets, asset => (isPlainObject(asset) ? asset.path : asset)); +import test from "ava"; +import { isPlainObject, sortBy } from "lodash-es"; +import { temporaryDirectory } from "tempy"; +import cpy from "cpy"; -const fixtures = 'test/fixtures/files'; +import globAssets from "../lib/glob-assets.js"; -test('Retrieve file from single path', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['upload.txt']); +const sortAssets = (assets) => + sortBy(assets, (asset) => (isPlainObject(asset) ? asset.path : asset)); - t.deepEqual(globbedAssets, ['upload.txt']); +const fixtures = "test/fixtures/files"; + +test("Retrieve file from single path", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, ["upload.txt"]); + + t.deepEqual(globbedAssets, ["upload.txt"]); }); -test('Retrieve multiple files from path', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['upload.txt', 'upload_other.txt']); +test("Retrieve multiple files from path", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + "upload.txt", + "upload_other.txt", + ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload_other.txt', 'upload.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload_other.txt", "upload.txt"]), + ); }); -test('Include missing files as defined, using Object definition', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['upload.txt', {path: 'miss*.txt', label: 'Missing'}]); +test("Include missing files as defined, using Object definition", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + "upload.txt", + { path: "miss*.txt", label: "Missing" }, + ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload.txt', {path: 'miss*.txt', label: 'Missing'}])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload.txt", { path: "miss*.txt", label: "Missing" }]), + ); }); -test('Retrieve multiple files from Object', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [ - {path: 'upload.txt', name: 'upload_name', label: 'Upload label'}, - 'upload_other.txt', +test("Retrieve multiple files from Object", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + { path: "upload.txt", name: "upload_name", label: "Upload label" }, + "upload_other.txt", ]); t.deepEqual( sortAssets(globbedAssets), - sortAssets([{path: 'upload.txt', name: 'upload_name', label: 'Upload label'}, 'upload_other.txt']) + sortAssets([ + { path: "upload.txt", name: "upload_name", label: "Upload label" }, + "upload_other.txt", + ]), ); }); -test('Retrieve multiple files without duplicates', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [ - 'upload_other.txt', - 'upload.txt', - 'upload_other.txt', - 'upload.txt', - 'upload.txt', - 'upload_other.txt', +test("Retrieve multiple files without duplicates", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + "upload_other.txt", + "upload.txt", + "upload_other.txt", + "upload.txt", + "upload.txt", + "upload_other.txt", ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload_other.txt', 'upload.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload_other.txt", "upload.txt"]), + ); }); -test('Favor Object over String values when removing duplicates', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [ - 'upload_other.txt', - 'upload.txt', - {path: 'upload.txt', name: 'upload_name'}, - 'upload.txt', - {path: 'upload_other.txt', name: 'upload_other_name'}, - 'upload.txt', - 'upload_other.txt', +test("Favor Object over String values when removing duplicates", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + "upload_other.txt", + "upload.txt", + { path: "upload.txt", name: "upload_name" }, + "upload.txt", + { path: "upload_other.txt", name: "upload_other_name" }, + "upload.txt", + "upload_other.txt", ]); t.deepEqual( sortAssets(globbedAssets), - sortAssets([{path: 'upload.txt', name: 'upload_name'}, {path: 'upload_other.txt', name: 'upload_other_name'}]) + sortAssets([ + { path: "upload.txt", name: "upload_name" }, + { path: "upload_other.txt", name: "upload_other_name" }, + ]), ); }); -test('Retrieve file from single glob', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['upload.*']); +test("Retrieve file from single glob", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, ["upload.*"]); - t.deepEqual(globbedAssets, ['upload.txt']); + t.deepEqual(globbedAssets, ["upload.txt"]); }); -test('Retrieve multiple files from single glob', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['*.txt']); +test("Retrieve multiple files from single glob", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, ["*.txt"]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload_other.txt', 'upload.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload_other.txt", "upload.txt"]), + ); }); -test('Accept glob array with one value', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [['*load.txt'], ['*_other.txt']]); +test("Accept glob array with one value", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + ["*load.txt"], + ["*_other.txt"], + ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload_other.txt', 'upload.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload_other.txt", "upload.txt"]), + ); }); -test('Include globs that resolve to no files as defined', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [['upload.txt', '!upload.txt']]); +test("Include globs that resolve to no files as defined", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + ["upload.txt", "!upload.txt"], + ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['!upload.txt', 'upload.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["!upload.txt", "upload.txt"]), + ); }); -test('Accept glob array with one value for missing files', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [['*missing.txt'], ['*_other.txt']]); +test("Accept glob array with one value for missing files", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + ["*missing.txt"], + ["*_other.txt"], + ]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['upload_other.txt', '*missing.txt'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets(["upload_other.txt", "*missing.txt"]), + ); }); -test('Replace name by filename for Object that match multiple files', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [{path: '*.txt', name: 'upload_name', label: 'Upload label'}]); +test("Replace name by filename for Object that match multiple files", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + { path: "*.txt", name: "upload_name", label: "Upload label" }, + ]); t.deepEqual( sortAssets(globbedAssets), sortAssets([ - {path: 'upload.txt', name: 'upload.txt', label: 'Upload label'}, - {path: 'upload_other.txt', name: 'upload_other.txt', label: 'Upload label'}, - ]) + { path: "upload.txt", name: "upload.txt", label: "Upload label" }, + { + path: "upload_other.txt", + name: "upload_other.txt", + label: "Upload label", + }, + ]), ); }); -test('Include dotfiles', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['.dot*']); +test("Include dotfiles", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [".dot*"]); - t.deepEqual(globbedAssets, ['.dotfile']); + t.deepEqual(globbedAssets, [".dotfile"]); }); -test('Ingnore single negated glob', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['!*.txt']); +test("Ingnore single negated glob", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, ["!*.txt"]); t.deepEqual(globbedAssets, []); }); -test('Ingnore single negated glob in Object', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [{path: '!*.txt'}]); +test("Ingnore single negated glob in Object", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [{ path: "!*.txt" }]); t.deepEqual(globbedAssets, []); }); -test('Accept negated globs', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, [['*.txt', '!**/*_other.txt']]); +test("Accept negated globs", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + ["*.txt", "!**/*_other.txt"], + ]); - t.deepEqual(globbedAssets, ['upload.txt']); + t.deepEqual(globbedAssets, ["upload.txt"]); }); -test('Expand directories', async t => { - const cwd = tempy.directory(); - await copy(fixtures, path.resolve(cwd, 'dir')); - const globbedAssets = await globAssets({cwd}, [['dir']]); +test("Expand directories", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, resolve(cwd, "dir"), { dot: true }); + const globbedAssets = await globAssets({ cwd }, [["dir"]]); - t.deepEqual(sortAssets(globbedAssets), sortAssets(['dir', 'dir/upload_other.txt', 'dir/upload.txt', 'dir/.dotfile'])); + t.deepEqual( + sortAssets(globbedAssets), + sortAssets([ + "dir", + "dir/upload_other.txt", + "dir/upload.txt", + "dir/.dotfile", + ]), + ); }); -test('Include empty directory as defined', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - await ensureDir(path.resolve(cwd, 'empty')); - const globbedAssets = await globAssets({cwd}, [['empty']]); +test("Include empty temporaryDirectory as defined", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + await mkdir(resolve(cwd, "empty"), { recursive: true }); + const globbedAssets = await globAssets({ cwd }, [["empty"]]); - t.deepEqual(globbedAssets, ['empty']); + t.deepEqual(globbedAssets, ["empty"]); }); -test('Deduplicate resulting files path', async t => { - const cwd = tempy.directory(); - await copy(fixtures, cwd); - const globbedAssets = await globAssets({cwd}, ['./upload.txt', path.resolve(cwd, 'upload.txt'), 'upload.txt']); +test("Deduplicate resulting files path", async (t) => { + const cwd = temporaryDirectory(); + await cpy(fixtures, cwd, { dot: true }); + const globbedAssets = await globAssets({ cwd }, [ + "./upload.txt", + resolve(cwd, "upload.txt"), + "upload.txt", + ]); t.is(globbedAssets.length, 1); }); diff --git a/test/helpers/mock-github.js b/test/helpers/mock-github.js deleted file mode 100644 index 6203d24d..00000000 --- a/test/helpers/mock-github.js +++ /dev/null @@ -1,43 +0,0 @@ -import nock from 'nock'; - -/** - * Retun a `nock` object setup to respond to a github authentication request. Other expectation and responses can be chained. - * - * @param {Object} [env={}] Environment variables. - * @param {String} [githubToken=env.GH_TOKEN || env.GITHUB_TOKEN || 'GH_TOKEN'] The github token to return in the authentication response. - * @param {String} [githubUrl=env.GH_URL || env.GITHUB_URL || 'https://api.github.com'] The url on which to intercept http requests. - * @param {String} [githubApiPathPrefix=env.GH_PREFIX || env.GITHUB_PREFIX || ''] The GitHub Enterprise API prefix. - * @return {Object} A `nock` object ready to respond to a github authentication request. - */ -export function authenticate( - env = {}, - { - githubToken = env.GH_TOKEN || env.GITHUB_TOKEN || 'GH_TOKEN', - githubUrl = env.GH_URL || env.GITHUB_URL || 'https://api.github.com', - githubApiPathPrefix = env.GH_PREFIX || env.GITHUB_PREFIX || '', - } = {} -) { - return nock(`${githubUrl}/${githubApiPathPrefix}`, {reqheaders: {Authorization: `token ${githubToken}`}}); -} - -/** - * Retun a `nock` object setup to respond to a github release upload request. Other expectation and responses can be chained. - * - * @param {Object} [env={}] Environment variables. - * @param {String} [githubToken=env.GH_TOKEN || env.GITHUB_TOKEN || 'GH_TOKEN'] The github token to return in the authentication response. - * @param {String} [uploadUrl] The url on which to intercept http requests. - * @return {Object} A `nock` object ready to respond to a github file upload request. - */ -export function upload( - env = {}, - { - githubToken = env.GH_TOKEN || env.GITHUB_TOKEN || 'GH_TOKEN', - uploadUrl, - contentType = 'text/plain', - contentLength, - } = {} -) { - return nock(uploadUrl, { - reqheaders: {Authorization: `token ${githubToken}`, 'content-type': contentType, 'content-length': contentLength}, - }); -} diff --git a/test/helpers/rate-limit.js b/test/helpers/rate-limit.js deleted file mode 100644 index fb78bf93..00000000 --- a/test/helpers/rate-limit.js +++ /dev/null @@ -1,7 +0,0 @@ -const RETRY_CONF = {retries: 3, factor: 1, minTimeout: 1, maxTimeout: 1}; - -const RATE_LIMITS = {search: 1, core: {read: 1, write: 1}}; - -const GLOBAL_RATE_LIMIT = 1; - -export default {RETRY_CONF, RATE_LIMITS, GLOBAL_RATE_LIMIT}; diff --git a/test/helpers/test-octokit.js b/test/helpers/test-octokit.js new file mode 100644 index 00000000..c55cb6a5 --- /dev/null +++ b/test/helpers/test-octokit.js @@ -0,0 +1,19 @@ +import { Octokit } from "@octokit/core"; +import { paginateRest } from "@octokit/plugin-paginate-rest"; + +const debugPlugin = (octokit) => { + octokit.hook.wrap("request", (request, options) => { + const { method, url, request: _ignore, ...rest } = options; + if (process.env.DEBUG) { + console.log("DEBUG: %s %s with", method, url, rest); + } + + return request(options); + }); +}; + +export const TestOctokit = Octokit.plugin(paginateRest, debugPlugin).defaults({ + baseUrl: "https://api.github.local", + userAgent: "test", + auth: "github_token", +}); diff --git a/test/integration.test.js b/test/integration.test.js index 96d44176..ffcae3ed 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -1,436 +1,1091 @@ -import path from 'path'; -import {escape} from 'querystring'; -import test from 'ava'; -import {stat} from 'fs-extra'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import clearModule from 'clear-module'; -import SemanticReleaseError from '@semantic-release/error'; -import {authenticate, upload} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; - -const cwd = 'test/fixtures/files'; -const client = proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}); - -test.beforeEach(t => { - // Clear npm cache to refresh the module state - clearModule('..'); - t.context.m = proxyquire('..', { - './lib/verify': proxyquire('../lib/verify', {'./get-client': client}), - './lib/publish': proxyquire('../lib/publish', {'./get-client': client}), - './lib/success': proxyquire('../lib/success', {'./get-client': client}), - './lib/fail': proxyquire('../lib/fail', {'./get-client': client}), - }); +import test from "ava"; +import sinon from "sinon"; +import SemanticReleaseError from "@semantic-release/error"; +import fetchMock from "fetch-mock"; +import { RELEASE_FAIL_LABEL } from "../lib/definitions/constants.js"; + +import { TestOctokit } from "./helpers/test-octokit.js"; + +const cwd = "test/fixtures/files"; +let cacheBuster = 0; + +test.beforeEach(async (t) => { + t.context.m = await import(`../index.js?${++cacheBuster}`); // Stub the logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; }); -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); -}); +test("Verify GitHub auth", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const options = { + repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, + }; -test.serial('Verify GitHub auth', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const options = {repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); - await t.notThrowsAsync(t.context.m.verifyConditions({}, {cwd, env, options, logger: t.context.logger})); + await t.notThrowsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify GitHub auth with publish options', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Verify GitHub auth with publish options", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const options = { - publish: {path: '@semantic-release/github'}, + publish: { path: "@semantic-release/github" }, repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + const fetch = fetchMock + .sandbox() + .get(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); - await t.notThrowsAsync(t.context.m.verifyConditions({}, {cwd, env, options, logger: t.context.logger})); + await t.notThrowsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify GitHub auth and assets config', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Verify GitHub auth and assets config", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const assets = [ - {path: 'lib/file.js'}, - 'file.js', - ['dist/**'], - ['dist/**', '!dist/*.js'], - {path: ['dist/**', '!dist/*.js']}, + { path: "lib/file.js" }, + "file.js", + ["dist/**"], + ["dist/**", "!dist/*.js"], + { path: ["dist/**", "!dist/*.js"] }, ]; const options = { - publish: [{path: '@semantic-release/npm'}], + publish: [{ path: "@semantic-release/npm" }], repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, }; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); - await t.notThrowsAsync(t.context.m.verifyConditions({assets}, {cwd, env, options, logger: t.context.logger})); + await t.notThrowsAsync( + t.context.m.verifyConditions( + { assets }, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Throw SemanticReleaseError if invalid config', async t => { +test("Throw SemanticReleaseError if invalid config", async (t) => { const env = {}; - const assets = [{wrongProperty: 'lib/file.js'}]; + const assets = [{ wrongProperty: "lib/file.js" }]; const successComment = 42; const failComment = 42; const failTitle = 42; const labels = 42; const assignees = 42; + const discussionCategoryName = 42; const options = { publish: [ - {path: '@semantic-release/npm'}, - {path: '@semantic-release/github', assets, successComment, failComment, failTitle, labels, assignees}, + { path: "@semantic-release/npm" }, + { + path: "@semantic-release/github", + assets, + successComment, + failComment, + failTitle, + labels, + assignees, + discussionCategoryName, + }, ], - repositoryUrl: 'invalid_url', + repositoryUrl: "invalid_url", }; - const errors = [ - ...(await t.throwsAsync(t.context.m.verifyConditions({}, {cwd, env, options, logger: t.context.logger}))), - ]; + const { errors } = await t.throwsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); - t.is(errors[0].name, 'SemanticReleaseError'); - t.is(errors[0].code, 'EINVALIDASSETS'); - t.is(errors[1].name, 'SemanticReleaseError'); - t.is(errors[1].code, 'EINVALIDSUCCESSCOMMENT'); - t.is(errors[2].name, 'SemanticReleaseError'); - t.is(errors[2].code, 'EINVALIDFAILTITLE'); - t.is(errors[3].name, 'SemanticReleaseError'); - t.is(errors[3].code, 'EINVALIDFAILCOMMENT'); - t.is(errors[4].name, 'SemanticReleaseError'); - t.is(errors[4].code, 'EINVALIDLABELS'); - t.is(errors[5].name, 'SemanticReleaseError'); - t.is(errors[5].code, 'EINVALIDASSIGNEES'); - t.is(errors[6].name, 'SemanticReleaseError'); - t.is(errors[6].code, 'EINVALIDGITHUBURL'); - t.is(errors[7].name, 'SemanticReleaseError'); - t.is(errors[7].code, 'ENOGHTOKEN'); + t.is(errors[0].name, "SemanticReleaseError"); + t.is(errors[0].code, "EINVALIDASSETS"); + t.is(errors[1].name, "SemanticReleaseError"); + t.is(errors[1].code, "EINVALIDSUCCESSCOMMENT"); + t.is(errors[2].name, "SemanticReleaseError"); + t.is(errors[2].code, "EINVALIDFAILTITLE"); + t.is(errors[3].name, "SemanticReleaseError"); + t.is(errors[3].code, "EINVALIDFAILCOMMENT"); + t.is(errors[4].name, "SemanticReleaseError"); + t.is(errors[4].code, "EINVALIDLABELS"); + t.is(errors[5].name, "SemanticReleaseError"); + t.is(errors[5].code, "EINVALIDASSIGNEES"); + t.is(errors[6].name, "SemanticReleaseError"); + t.is(errors[6].code, "EINVALIDDISCUSSIONCATEGORYNAME"); + t.is(errors[7].name, "SemanticReleaseError"); + t.is(errors[7].code, "EINVALIDGITHUBURL"); + t.is(errors[8].name, "SemanticReleaseError"); + t.is(errors[8].code, "ENOGHTOKEN"); }); -test.serial('Publish a release with an array of assets', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a release with an array of assets", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const assets = [ - {path: ['upload.txt'], name: 'upload_file_name.txt'}, - {path: ['upload_other.txt'], name: 'other_file.txt', label: 'Other File'}, + { path: ["upload.txt"], name: "upload_file_name.txt" }, + { path: ["upload_other.txt"], name: "other_file.txt", label: "Other File" }, ]; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/upload.txt`; const otherAssetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/other_file.txt`; const releaseId = 1; + const uploadOrigin = "https://github.com"; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; - const uploadUrl = `https://github.com${uploadUri}{?name,label}`; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) - .reply(200, {html_url: releaseUrl}); - const githubUpload1 = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, 'upload.txt'))).size, - }) - .post(`${uploadUri}?name=${escape('upload_file_name.txt')}`) - .reply(200, {browser_download_url: assetUrl}); - const githubUpload2 = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, 'upload_other.txt'))).size, - }) - .post(`${uploadUri}?name=${escape('other_file.txt')}&label=${escape('Other File')}`) - .reply(200, {browser_download_url: otherAssetUrl}); - - const result = await t.context.m.publish({assets}, {cwd, env, options, nextRelease, logger: t.context.logger}); + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { + body: { draft: false }, + }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + "upload_file_name.txt", + )}&`, + { + browser_download_url: assetUrl, + }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + "other_file.txt", + )}&label=${encodeURIComponent("Other File")}`, + { browser_download_url: otherAssetUrl }, + ); + + const result = await t.context.m.publish( + { assets }, + { + cwd, + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Published file %s', otherAssetUrl)); - t.true(t.context.log.calledWith('Published file %s', assetUrl)); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(github.isDone()); - t.true(githubUpload1.isDone()); - t.true(githubUpload2.isDone()); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true(t.context.log.calledWith("Published file %s", otherAssetUrl)); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true(fetch.done()); }); -test.serial('Publish a release with release information in assets', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a release with release information in assets", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const assets = [ { - path: ['upload.txt'], + path: ["upload.txt"], name: `file_with_release_\${nextRelease.gitTag}_in_filename.txt`, label: `File with release \${nextRelease.gitTag} in label`, }, ]; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/file_with_release_v1.0.0_in_filename.txt`; const releaseId = 1; + const uploadOrigin = "https://github.com"; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; - const uploadUrl = `https://github.com${uploadUri}{?name,label}`; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { upload_url: uploadUrl, html_url: releaseUrl, id: releaseId }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.gitTag, + body: nextRelease.notes, + draft: true, + prerelease: true, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { + body: { draft: false }, + }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + "file_with_release_v1.0.0_in_filename.txt", + )}&label=${encodeURIComponent("File with release v1.0.0 in label")}`, + { browser_download_url: assetUrl }, + ); + + const result = await t.context.m.publish( + { assets }, + { + cwd, + env, + options, + branch: { type: "release" }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true(fetch.done()); +}); + +test("Update a release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, }) - .reply(200, {html_url: releaseUrl}); - const githubUpload = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, 'upload.txt'))).size, - }) - .post( - `${uploadUri}?name=${escape('file_with_release_v1.0.0_in_filename.txt')}&label=${escape( - 'File with release v1.0.0 in label' - )}` + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { id: releaseId }, ) - .reply(200, {browser_download_url: assetUrl}); + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ); - const result = await t.context.m.publish({assets}, {cwd, env, options, nextRelease, logger: t.context.logger}); + const result = await t.context.m.addChannel( + {}, + { + cwd, + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Published file %s', assetUrl)); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(github.isDone()); - t.true(githubUpload.isDone()); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.deepEqual(t.context.log.args[1], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Comment and add labels on PR included in the releases', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const prs = [{number: 1, pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) +test("Comment and add labels on PR included in the releases", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + full_name: `${owner}/${repo}`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, ) - .reply(200, {items: []}); + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + (url, { body }) => { + t.is( + url, + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + ); + + const data = JSON.parse(body); + t.regex(data.body, /This PR is included/); - await t.context.m.success({failTitle}, {cwd, env, options, commits, nextRelease, releases, logger: t.context.logger}); + return true; + }, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(github.isDone()); + await t.context.m.success( + { failTitle }, + { + cwd, + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 1), + ); + t.true(fetch.done()); }); -test.serial('Open a new issue with the list of errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Open a new issue with the list of errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) + + const fetch = fetchMock + .sandbox() .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - labels: ['semantic-release'], + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + full_name: `${owner}/${repo}`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }, + { repeat: 2 }, + ) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, `https://api.github.local/repos/${owner}/${repo}/issues`); + const data = JSON.parse(body); + t.is(data.title, failTitle); + t.regex( + data.body, + /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, + ); + t.deepEqual(data.labels, ["semantic-release", RELEASE_FAIL_LABEL]); - await t.context.m.fail({failTitle}, {cwd, env, options, errors, logger: t.context.logger}); + return true; + }, + { html_url: "https://github.com/issues/1", number: 1 }, + ); + + await t.context.m.fail( + { failTitle }, + { + cwd, + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); -test.serial('Verify, release and notify success', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const assets = ['upload.txt', {path: 'upload_other.txt', name: 'other_file.txt', label: 'Other File'}]; - const failTitle = 'The automated release is failing 🚨'; +test("Verify, release and notify success", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const assets = [ + "upload.txt", + { path: "upload_other.txt", name: "other_file.txt", label: "Other File" }, + ]; + const failTitle = "The automated release is failing 🚨"; const options = { - publish: [{path: '@semantic-release/npm'}, {path: '@semantic-release/github', assets}], - branch: 'master', + publish: [ + { path: "@semantic-release/npm" }, + { path: "@semantic-release/github", assets }, + ], repositoryUrl: `https://github.com/${owner}/${repo}.git`, }; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/upload.txt`; const otherAssetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/other_file.txt`; const releaseId = 1; + const uploadOrigin = "https://github.com"; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; - const uploadUrl = `https://github.com${uploadUri}{?name,label}`; - const prs = [{number: 1, pull_request: {}, state: 'closed'}]; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, - }) - .reply(200, {html_url: releaseUrl}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + const prs = [{ number: 1, pull_request: true, state: "closed" }]; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + + const fetch = fetchMock + .sandbox() .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + full_name: `${owner}/${repo}`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getSRIssues("), + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { body: { draft: false } }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["released"] }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent("upload.txt")}&`, + { browser_download_url: assetUrl }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=other_file.txt&label=${encodeURIComponent( + "Other File", + )}`, + { + browser_download_url: otherAssetUrl, + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { + html_url: "https://github.com/successcomment-1", + }, + ); + + await t.notThrowsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + await t.context.m.publish( + { assets }, + { + cwd, + env, + options, + branch: { type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + await t.context.m.success( + { assets, failTitle }, + { + cwd, + env, + options, + nextRelease, + commits, + releases: [], + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true(t.context.log.calledWith("Published file %s", otherAssetUrl)); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true(fetch.done()); +}); + +test("Verify, update release and notify success", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const options = { + publish: [ + { path: "@semantic-release/npm" }, + { path: "@semantic-release/github" }, + ], + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const prs = [{ number: 1, pull_request: true, state: "closed" }]; + const commits = [ + { hash: "123", message: "Commit 1 message", tree: { long: "aaa" } }, + ]; + + const fetch = fetchMock + .sandbox() .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - const githubUpload1 = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, 'upload.txt'))).size, - }) - .post(`${uploadUri}?name=${escape('upload.txt')}`) - .reply(200, {browser_download_url: assetUrl}); - const githubUpload2 = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, 'upload_other.txt'))).size, - }) - .post(`${uploadUri}?name=${escape('other_file.txt')}&label=${escape('Other File')}`) - .reply(200, {browser_download_url: otherAssetUrl}); - - await t.notThrowsAsync(t.context.m.verifyConditions({}, {cwd, env, options, logger: t.context.logger})); - await t.context.m.publish({assets}, {cwd, env, options, nextRelease, logger: t.context.logger}); + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + full_name: `${owner}/${repo}`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/tags/${nextRelease.gitTag}`, + { id: releaseId }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { + body: { + tag_name: nextRelease.gitTag, + name: nextRelease.name, + prerelease: false, + }, + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { + html_url: "https://github.com/successcomment-1", + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await t.notThrowsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + await t.context.m.addChannel( + {}, + { + cwd, + env, + branch: { type: "release", main: true }, + nextRelease, + options, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); await t.context.m.success( - {assets, failTitle}, - {cwd, env, options, nextRelease, commits, releases: [], logger: t.context.logger} + { failTitle }, + { + cwd, + env, + options, + nextRelease, + commits, + releases: [], + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, ); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Published file %s', otherAssetUrl)); - t.true(t.context.log.calledWith('Published file %s', assetUrl)); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(github.isDone()); - t.true(githubUpload1.isDone()); - t.true(githubUpload2.isDone()); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.deepEqual(t.context.log.args[1], [ + "Updated GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Verify and notify failure', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test("Verify and notify failure", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const errors = [ - new SemanticReleaseError('Error message 1', 'ERR1', 'Error 1 details'), - new SemanticReleaseError('Error message 2', 'ERR2', 'Error 2 details'), - new SemanticReleaseError('Error message 3', 'ERR3', 'Error 3 details'), + new SemanticReleaseError("Error message 1", "ERR1", "Error 1 details"), + new SemanticReleaseError("Error message 2", "ERR2", "Error 2 details"), + new SemanticReleaseError("Error message 3", "ERR3", "Error 3 details"), ]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) + + const fetch = fetchMock + .sandbox() .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}) - .post(`/repos/${owner}/${repo}/issues`, { - title: failTitle, - body: /---\n\n### Error message 1\n\nError 1 details\n\n---\n\n### Error message 2\n\nError 2 details\n\n---\n\n### Error message 3\n\nError 3 details\n\n---/, - labels: ['semantic-release'], + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + full_name: `${owner}/${repo}`, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }, + { + repeat: 2, + }, + ) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, }) - .reply(200, {html_url: 'https://github.com/issues/1', number: 1}); + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .postOnce(`https://api.github.local/repos/${owner}/${repo}/issues`, { + html_url: "https://github.com/issues/1", + number: 1, + }); - await t.notThrowsAsync(t.context.m.verifyConditions({}, {cwd, env, options, logger: t.context.logger})); - await t.context.m.fail({failTitle}, {cwd, env, options, errors, logger: t.context.logger}); + await t.notThrowsAsync( + t.context.m.verifyConditions( + {}, + { cwd, env, options, logger: t.context.logger }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + await t.context.m.fail( + { failTitle }, + { + cwd, + env, + options, + branch: { name: "master" }, + errors, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication']); - t.true(t.context.log.calledWith('Created issue #%d: %s.', 1, 'https://github.com/issues/1')); - t.true(github.isDone()); + t.deepEqual(t.context.log.args[0], ["Verify GitHub authentication"]); + t.true( + t.context.log.calledWith( + "Created issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(fetch.done()); }); diff --git a/test/is-prerelease.test.js b/test/is-prerelease.test.js new file mode 100644 index 00000000..8c2c60f1 --- /dev/null +++ b/test/is-prerelease.test.js @@ -0,0 +1,52 @@ +import test from "ava"; +import isPrerelease from "../lib/is-prerelease.js"; + +test("Test for empty object", (t) => { + const branch = {}; + t.is(isPrerelease(branch), false); +}); + +test("Test if prerelease true property as boolean is used correctly", (t) => { + const branch = { + prerelease: true, + }; + t.is(isPrerelease(branch), true); +}); + +test("Test if prerelease false property as boolean is used correctly", (t) => { + const branch = { + prerelease: false, + }; + t.is(isPrerelease(branch), false); +}); + +test("Test if prerelease property as string is used correctly", (t) => { + const branch = { + prerelease: "rc", + }; + t.is(isPrerelease(branch), true); +}); + +test("Test if prerelease type is used correctly", (t) => { + const branch = { + type: "prerelease", + }; + t.is(isPrerelease(branch), true); +}); + +test("Test if prerelease type and main is used correctly", (t) => { + const branch = { + type: "release", + main: false, + }; + t.is(isPrerelease(branch), true); +}); + +test("Test if prerelease type and main in addition to prerelease is used correctly", (t) => { + const branch = { + type: "release", + main: false, + prerelease: false, + }; + t.is(isPrerelease(branch), false); +}); diff --git a/test/publish.test.js b/test/publish.test.js index 059b9792..3009068a 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -1,252 +1,1091 @@ -import path from 'path'; -import {escape} from 'querystring'; -import test from 'ava'; -import {stat} from 'fs-extra'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import tempy from 'tempy'; -import {authenticate, upload} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; +import sinon from "sinon"; +import { temporaryDirectory } from "tempy"; +import test from "ava"; +import fetchMock from "fetch-mock"; + +import { TestOctokit } from "./helpers/test-octokit.js"; /* eslint camelcase: ["error", {properties: "never"}] */ -const cwd = 'test/fixtures/files'; -const publish = proxyquire('../lib/publish', { - './get-client': proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}), -}); +import publish from "../lib/publish.js"; + +const cwd = "test/fixtures/files"; -test.beforeEach(t => { +test.beforeEach((t) => { // Mock logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; }); -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); +test("Publish a release without creating discussion", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Publish a release', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a release and create discussion", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { discussionCategoryName: "Announcements" }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + version: "v1.0.0", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + const discussionId = 1; + const discussionUrl = `https://github.com/${owner}/${repo}/discussions/${discussionId}`; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + discussion_url: discussionUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + discussion_category_name: pluginConfig.discussionCategoryName, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.deepEqual(t.context.log.args[1], [ + "Created GitHub release discussion: %s", + discussionUrl, + ]); + t.true(fetch.done()); +}); + +test("Publish a release on a channel", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const pluginConfig = {}; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const releaseId = 1; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; - const github = authenticate(env) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl, id: releaseId}); + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: true, + }, + }, + ); - const result = await publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger}); + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", channel: "next", main: false }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.deepEqual(t.context.log.args[0], ['Published GitHub release: %s', releaseUrl]); - t.true(github.isDone()); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Publish a release, retrying 4 times', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a prerelease wihtout creating discussion", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const pluginConfig = {}; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const releaseId = 1; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; - const github = authenticate(env) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - }) - .times(3) - .reply(404) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl, id: releaseId}); - - const result = await publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger}); + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: true, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "prerelease", channel: "beta" }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.deepEqual(t.context.log.args[0], ['Published GitHub release: %s', releaseUrl]); - t.true(github.isDone()); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Publish a release with one asset', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a prerelease and create discussion", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { discussionCategoryName: "Announcements" }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + const discussionId = 1; + const discussionUrl = `https://github.com/${owner}/${repo}/discussions/${discussionId}`; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + discussion_url: discussionUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: true, + discussion_category_name: pluginConfig.discussionCategoryName, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "prerelease", channel: "beta" }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.deepEqual(t.context.log.args[1], [ + "Created GitHub release discussion: %s", + discussionUrl, + ]); + t.true(fetch.done()); +}); + +test("Publish a maintenance release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = {}; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { + name: "test_branch", + type: "maintenance", + channel: "1.x", + main: false, + }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Publish a release with one asset", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; const pluginConfig = { - assets: [['**', '!**/*.txt'], {path: '.dotfile', label: 'A dotfile with no ext'}], + assets: [ + ["**", "!**/*.txt"], + { path: ".dotfile", label: "A dotfile with no ext" }, + ], + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", }; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const untaggedReleaseUrl = `https://github.com/${owner}/${repo}/releases/untagged-123`; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/.dotfile`; const releaseId = 1; + const uploadOrigin = `https://uploads.github.local`; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; - const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock + .sandbox() + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: untaggedReleaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { upload_url: uploadUrl, html_url: releaseUrl }, + { body: { draft: false } }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + ".dotfile", + )}&label=${encodeURIComponent("A dotfile with no ext")}`, + { browser_download_url: assetUrl }, + ); - const github = authenticate(env) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, - }) - .reply(200, {upload_url: uploadUrl, html_url: untaggedReleaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl}); - - const githubUpload = upload(env, { - uploadUrl: 'https://github.com', - contentLength: (await stat(path.resolve(cwd, '.dotfile'))).size, - }) - .post(`${uploadUri}?name=${escape('.dotfile')}&label=${escape('A dotfile with no ext')}`) - .reply(200, {browser_download_url: assetUrl}); - - const result = await publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger}); + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(t.context.log.calledWith('Published file %s', assetUrl)); - t.true(github.isDone()); - t.true(githubUpload.isDone()); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(fetch.done()); }); -test.serial('Publish a release with one asset and custom github url', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_URL: 'https://othertesturl.com:443', GH_TOKEN: 'github_token', GH_PREFIX: 'prefix'}; +test("Publish a release with one asset and custom github url", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GH_URL: "https://othertesturl.com:443", + GH_TOKEN: "github_token", + GH_PREFIX: "prefix", + }; const pluginConfig = { - assets: [['*.txt', '!**/*_other.txt'], {path: ['*.txt', '!**/*_other.txt'], label: 'A text file'}, 'upload.txt'], + assets: [ + ["*.txt", "!**/*_other.txt"], + { path: ["*.txt", "!**/*_other.txt"], label: "A text file" }, + "upload.txt", + ], + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, }; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; const untaggedReleaseUrl = `${env.GH_URL}/${owner}/${repo}/releases/untagged-123`; const releaseUrl = `${env.GH_URL}/${owner}/${repo}/releases/${nextRelease.version}`; const assetUrl = `${env.GH_URL}/${owner}/${repo}/releases/download/${nextRelease.version}/upload.txt`; const releaseId = 1; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; const uploadUrl = `${env.GH_URL}${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock + .sandbox() + .postOnce( + `${env.GH_URL}/prefix/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: untaggedReleaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `${env.GH_URL}/prefix/repos/${owner}/${repo}/releases/${releaseId}`, + { upload_url: uploadUrl, html_url: releaseUrl }, + { body: { draft: false } }, + ) + .postOnce( + `${env.GH_URL}${uploadUri}?name=${encodeURIComponent( + "upload.txt", + )}&label=${encodeURIComponent("A text file")}`, + { + browser_download_url: assetUrl, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(fetch.done()); +}); + +test("Publish a release with an array of missing assets", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const emptyDirectory = temporaryDirectory(); + const pluginConfig = { + assets: [emptyDirectory, { path: "missing.txt", name: "missing.txt" }], + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const untaggedReleaseUrl = `https://github.com/${owner}/${repo}/releases/untagged-123`; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const assetUrl = `${env.GH_URL}/${owner}/${repo}/releases/download/${nextRelease.version}/upload.txt`; + const uploadOrigin = "https://uploads.github.com"; + const uploadUri = `/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock + .sandbox() + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: untaggedReleaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { html_url: releaseUrl }, + { body: { draft: false } }, + ); - const github = authenticate(env, {}) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, - }) - .reply(200, {upload_url: uploadUrl, html_url: untaggedReleaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, - }) - .reply(200, {upload_url: uploadUrl, html_url: releaseUrl}); - - const githubUpload = upload(env, { - uploadUrl: env.GH_URL, - contentLength: (await stat(path.resolve(cwd, 'upload.txt'))).size, - }) - .post(`${uploadUri}?name=${escape('upload.txt')}&label=${escape('A text file')}`) - .reply(200, {browser_download_url: assetUrl}); - - const result = await publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger}); + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(t.context.log.calledWith('Published file %s', assetUrl)); - t.true(github.isDone()); - t.true(githubUpload.isDone()); -}); - -test.serial('Publish a release with an array of missing assets', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const emptyDirectory = tempy.directory(); - const pluginConfig = {assets: [emptyDirectory, {path: 'missing.txt', name: 'missing.txt'}]}; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true( + t.context.error.calledWith( + "The asset %s cannot be read, and will be ignored.", + "missing.txt", + ), + ); + t.true( + t.context.error.calledWith( + "The asset %s is not a file, and will be ignored.", + emptyDirectory, + ), + ); + t.true(fetch.done()); +}); + +test("Publish a release with asset and create discussion", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { + assets: [ + ["**", "!**/*.txt"], + { path: ".dotfile", label: "A dotfile with no ext" }, + ], + discussionCategoryName: "Announcements", + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; const untaggedReleaseUrl = `https://github.com/${owner}/${repo}/releases/untagged-123`; const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/.dotfile`; + const releaseId = 1; + const uploadOrigin = `https://uploads.github.local`; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + const branch = "test_branch"; + const discussionId = 1; + const discussionUrl = `https://github.com/${owner}/${repo}/discussions/${discussionId}`; + + const fetch = fetchMock + .sandbox() + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: untaggedReleaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + discussion_url: discussionUrl, + }, + { + body: { + draft: false, + discussion_category_name: pluginConfig.discussionCategoryName, + }, + }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + ".dotfile", + )}&label=${encodeURIComponent("A dotfile with no ext")}`, + { browser_download_url: assetUrl }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.true(t.context.log.calledWith("Published GitHub release: %s", releaseUrl)); + t.true( + t.context.log.calledWith( + "Created GitHub release discussion: %s", + discussionUrl, + ), + ); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(fetch.done()); +}); + +test("Publish a draft release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { draftRelease: true }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; const releaseId = 1; const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; - const github = authenticate(env) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - draft: true, - }) - .reply(200, {upload_url: uploadUrl, html_url: untaggedReleaseUrl, id: releaseId}) - .patch(`/repos/${owner}/${repo}/releases/${releaseId}`, { - draft: false, - }) - .reply(200, {html_url: releaseUrl}); - - const result = await publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger}); + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); t.is(result.url, releaseUrl); - t.true(t.context.log.calledWith('Published GitHub release: %s', releaseUrl)); - t.true(t.context.error.calledWith('The asset %s cannot be read, and will be ignored.', 'missing.txt')); - t.true(t.context.error.calledWith('The asset %s is not a file, and will be ignored.', emptyDirectory)); - t.true(github.isDone()); + t.deepEqual(t.context.log.args[0], [ + "Created GitHub draft release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); -test.serial('Throw error without retries for 400 error', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; +test("Publish a draft release with one asset", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { + assets: [ + ["**", "!**/*.txt"], + { path: ".dotfile", label: "A dotfile with no ext" }, + ], + draftRelease: true, + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const assetUrl = `https://github.com/${owner}/${repo}/releases/download/${nextRelease.version}/.dotfile`; + const releaseId = 1; + const uploadOrigin = `https://uploads.github.local`; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `${uploadOrigin}${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock + .sandbox() + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + id: releaseId, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + draft: true, + prerelease: false, + }, + }, + ) + .postOnce( + `${uploadOrigin}${uploadUri}?name=${encodeURIComponent( + ".dotfile", + )}&label=${encodeURIComponent("A dotfile with no ext")}`, + { browser_download_url: assetUrl }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.true( + t.context.log.calledWith("Created GitHub draft release: %s", releaseUrl), + ); + t.true(t.context.log.calledWith("Published file %s", assetUrl)); + t.true(fetch.done()); +}); + +test("Publish a release when env.GITHUB_URL is set to https://github.com (Default in GitHub Actions, #268)", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GITHUB_TOKEN: "github_token", + GITHUB_URL: "https://github.com", + GITHUB_API_URL: "https://api.github.com", + }; const pluginConfig = {}; - const nextRelease = {version: '1.0.0', gitHead: '123', gitTag: 'v1.0.0', notes: 'Test release note body'}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - - const github = authenticate(env) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - }) - .reply(404) - .post(`/repos/${owner}/${repo}/releases`, { - tag_name: nextRelease.gitTag, - target_commitish: options.branch, - name: nextRelease.gitTag, - body: nextRelease.notes, - }) - .reply(400); - - const error = await t.throwsAsync(publish(pluginConfig, {cwd, env, options, nextRelease, logger: t.context.logger})); - - t.is(error.status, 400); - t.true(github.isDone()); + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.com/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: nextRelease.notes, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Publish a custom release body", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { + releaseBodyTemplate: + "To install this run npm install package@<%= nextRelease.name %>\n\n<%= nextRelease.notes %>", + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: nextRelease.name, + body: `To install this run npm install package@${nextRelease.name}\n\n${nextRelease.notes}`, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); +}); + +test("Publish a custom release name", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { + releaseNameTemplate: + "omg its the best release: <%= nextRelease.name %> 🌈🌈", + }; + const nextRelease = { + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const uploadUri = `/api/uploads/repos/${owner}/${repo}/releases/${releaseId}/assets`; + const uploadUrl = `https://github.com${uploadUri}{?name,label}`; + const branch = "test_branch"; + + const fetch = fetchMock.sandbox().postOnce( + `https://api.github.local/repos/${owner}/${repo}/releases`, + { + upload_url: uploadUrl, + html_url: releaseUrl, + }, + { + body: { + tag_name: nextRelease.gitTag, + target_commitish: branch, + name: `omg its the best release: ${nextRelease.name} 🌈🌈`, + body: nextRelease.notes, + prerelease: false, + }, + }, + ); + + const result = await publish( + pluginConfig, + { + cwd, + env, + options, + branch: { name: branch, type: "release", main: true }, + nextRelease, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.is(result.url, releaseUrl); + t.deepEqual(t.context.log.args[0], [ + "Published GitHub release: %s", + releaseUrl, + ]); + t.true(fetch.done()); }); diff --git a/test/success.test.js b/test/success.test.js index f326e531..6f8a4ed5 100644 --- a/test/success.test.js +++ b/test/success.test.js @@ -1,822 +1,4416 @@ -import {escape} from 'querystring'; -import test from 'ava'; -import {repeat} from 'lodash'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import ISSUE_ID from '../lib/definitions/sr-issue-id'; -import {authenticate} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; +import { repeat } from "lodash-es"; +import sinon from "sinon"; +import test from "ava"; +import fetchMock from "fetch-mock"; +import assert from "assert"; + +import { ISSUE_ID } from "../lib/definitions/constants.js"; +import getReleaseLinks from "../lib/get-release-links.js"; +import { TestOctokit } from "./helpers/test-octokit.js"; /* eslint camelcase: ["error", {properties: "never"}] */ -const success = proxyquire('../lib/success', { - './get-client': proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}), -}); +import success from "../lib/success.js"; + +test.beforeEach((t) => { + // Mock logger + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; +}); + +test("Add comment and labels to PRs associated with release commits and issues solved by PR/commits comments", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const redirectedOwner = "test_user_2"; + const redirectedRepo = "test_repo_2"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", body: "Fixes #3", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { + hash: "123", + message: "Commit 1 message\n\n Fix #1", + tree: { long: "aaa" }, + }, + { hash: "456", message: "Commit 2 message", tree: { long: "ccc" } }, + { + hash: "789", + message: `Commit 3 message Closes https://github.com/${redirectedOwner}/${redirectedRepo}/issues/4`, + tree: { long: "ccc" }, + }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${redirectedOwner}/${redirectedRepo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue3: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/3", + number: 3, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/3", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/pulls/2/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/1/comments`, + { + html_url: "https://github.com/successcomment-1", + }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/1/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/2/comments`, + { html_url: "https://github.com/successcomment-2" }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/2/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/3/comments`, + { html_url: "https://github.com/successcomment-3" }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/3/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${redirectedOwner}/${redirectedRepo}/issues/4/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent( + `repo:${redirectedOwner}/${redirectedRepo}`, + )}+${encodeURIComponent("type:issue")}+${encodeURIComponent( + "state:open", + )}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 1), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 2, + "https://github.com/successcomment-2", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 2), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 3, + "https://github.com/successcomment-3", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 3), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 4), + ); + t.true(fetch.done()); +}); + +test("Add comment and labels to PRs associated with release commits and issues (multipaged associatedPRs)", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", body: "Fixes #3", state: "closed" }, + { number: 5, __typename: "PullRequest", state: "closed" }, + { number: 6, __typename: "PullRequest", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { + hash: "123", + message: "Commit 1 message\n\n Fix #1", + tree: { long: "aaa" }, + }, + { hash: "456", message: "Commit 2 message", tree: { long: "ccc" } }, + { + hash: "789", + message: `Commit 3 message Closes https://github.com/${owner}/${repo}/issues/4`, + tree: { long: "ccc" }, + }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "YE", + hasNextPage: true, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + commit789: { + oid: "789", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[2]], + }, + }, + }, + }, + }) + .postOnce( + "https://api.github.local/graphql", + { + data: { + repository: { + commit: { + associatedPullRequests: { + pageInfo: { + endCursor: "NE", + hasNextPage: false, + }, + nodes: [prs[3]], + }, + }, + }, + }, + }, + { + overwriteRoutes: true, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue3: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/3", + number: 3, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/4", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/6/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3/comments`, + { html_url: "https://github.com/successcomment-3" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/6/comments`, + { html_url: "https://github.com/successcomment-6" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/6/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent( + `repo:${owner}/${repo}`, + )}+${encodeURIComponent("type:issue")}+${encodeURIComponent( + "state:open", + )}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 3, + "https://github.com/successcomment-3", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 3), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 4), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 6, + "https://github.com/successcomment-6", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 6), + ); + t.true(fetch.done()); +}); + +test("Add comment and labels to PRs associated with release commits and issues closed by PR/commits comments with custom URL", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GH_URL: "https://custom-url.com", + GH_TOKEN: "github_token", + GH_PREFIX: "prefix", + }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", body: "Fixes #3", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://custom-url.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message\n\n Fix #1" }, + { hash: "456", message: "Commit 2 message" }, + { + hash: "789", + message: `Commit 3 message Closes https://custom-url.com/${owner}/${repo}/issues/4`, + }, + ]; + const nextRelease = { version: "1.0.0", channel: "next" }; + const releases = [ + { name: "GitHub release", url: "https://custom-url.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://custom-url.com/prefix/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://custom-url.com/prefix/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://custom-url.com/prefix/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue3: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://custom-url.com/owner/repo/issues/3", + number: 3, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://custom-url.com/owner/repo/issues/4", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/1/comments`, + { + html_url: "https://custom-url.com/successcomment-1", + }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["released on @next"] }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/2/comments`, + { + html_url: "https://custom-url.com/successcomment-2", + }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/2/labels`, + {}, + { body: ["released on @next"] }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/3/comments`, + { + html_url: "https://custom-url.com/successcomment-3", + }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/3/labels`, + {}, + { body: ["released on @next"] }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/4/comments`, + { + html_url: "https://custom-url.com/successcomment-4", + }, + ) + .postOnce( + `https://custom-url.com/prefix/repos/${owner}/${repo}/issues/4/labels`, + {}, + { body: ["released on @next"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://custom-url.com/prefix/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://custom-url.com/prefix/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://custom-url.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith( + "Added labels %O to PR #%d", + ["released on @next"], + 1, + ), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 2, + "https://custom-url.com/successcomment-2", + ), + ); + t.true( + t.context.log.calledWith( + "Added labels %O to PR #%d", + ["released on @next"], + 2, + ), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 3, + "https://custom-url.com/successcomment-3", + ), + ); + t.true( + t.context.log.calledWith( + "Added labels %O to issue #%d", + ["released on @next"], + 3, + ), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://custom-url.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith( + "Added labels %O to issue #%d", + ["released on @next"], + 4, + ), + ); + t.true(fetch.done()); +}); + +test("Make multiple search queries if necessary", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", state: "closed" }, + { number: 3, __typename: "PullRequest", state: "closed" }, + { number: 4, __typename: "PullRequest", state: "closed" }, + { number: 5, __typename: "PullRequest", state: "closed" }, + { number: 6, __typename: "PullRequest", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: repeat("a", 40), message: "Commit 1 message" }, + { hash: repeat("b", 40), message: "Commit 2 message" }, + { hash: repeat("c", 40), message: "Commit 3 message" }, + { hash: repeat("d", 40), message: "Commit 4 message" }, + { hash: repeat("e", 40), message: "Commit 5 message" }, + { hash: repeat("f", 40), message: "Commit 6 message" }, + { hash: repeat("g", 40), message: "Commit 7 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commitaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: { + oid: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commitbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: { + oid: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + commitcccccccccccccccccccccccccccccccccccccccccc: { + oid: "cccccccccccccccccccccccccccccccccccccccccc", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[2]], + }, + }, + commiteeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee: { + oid: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[3]], + }, + }, + commitffffffffffffffffffffffffffffffffffffffffff: { + oid: "ffffffffffffffffffffffffffffffffffffffffff", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[4]], + }, + }, + commitgggggggggggggggggggggggggggggggggggggggggg: { + oid: "gggggggggggggggggggggggggggggggggggggggggg", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[5]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[1].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/3/commits`, + [{ sha: commits[2].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/4/commits`, + [{ sha: commits[3].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/5/commits`, + [{ sha: commits[4].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/6/commits`, + [{ sha: commits[5].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + { html_url: "https://github.com/successcomment-2" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3/comments`, + { html_url: "https://github.com/successcomment-3" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/comments`, + { html_url: "https://github.com/successcomment-5" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/6/comments`, + { html_url: "https://github.com/successcomment-6" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/6/labels`, + {}, + { + body: ["released"], + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 1), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 2, + "https://github.com/successcomment-2", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 2), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 3, + "https://github.com/successcomment-3", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 3), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 4), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 5, + "https://github.com/successcomment-5", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 5), + ); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 6, + "https://github.com/successcomment-6", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 6), + ); + t.true(fetch.done()); +}); + +test("Do not add comment and labels for unrelated PR returned by search (compare sha and merge_commit_sha)", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message" }, + { hash: "456", message: "Commit 2 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: "rebased_sha" }], + ) + .getOnce(`https://api.github.local/repos/${owner}/${repo}/pulls/1`, { + merge_commit_sha: commits[0].hash, + }) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: "rebased_sha" }], + ) + .getOnce(`https://api.github.local/repos/${owner}/${repo}/pulls/2`, { + merge_commit_sha: "unrelated_sha", + }) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { + html_url: "https://github.com/successcomment-1", + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 1), + ); + t.true(fetch.done()); +}); + +test("Do not add comment and labels if no PR is associated with release commits", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true(fetch.done()); +}); + +test("Do not add comment and labels if no commits is found for release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = []; + const nextRelease = { version: "1.1.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true(fetch.done()); + t.true(t.context.log.calledWith("No commits found in release")); + t.true( + t.context.log.calledWith("Skip commenting on issues and pull requests."), + ); +}); + +test("Do not add comment and labels to PR/issues from other repo", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message\n\n Fix other/other#1" }, + { hash: "456", message: `Commit 2 message Fix ${owner}/${repo}#2` }, + { hash: "789", message: "Commit 3 message Closes other/other#3" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + commit789: { + oid: "789", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + }, + }, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getRelatedIssues\(/); + return true; + }, + { + data: { + repository: { + issue2: { + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/14", + number: 2, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + { html_url: "https://github.com/successcomment-2" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 2, + "https://github.com/successcomment-2", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 2), + ); + t.true(fetch.done()); +}); + +test("Ignore missing and forbidden issues/PRs", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const prs = [ + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", body: "Fixes #4", state: "closed" }, + { number: 3, __typename: "PullRequest", body: "Fixes #5", state: "closed" }, + ]; + const options = { + branch: "master", + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message\n\n Fix #1" }, + { hash: "456", message: "Commit 2 message" }, + { hash: "789", message: "Commit 3 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + commit789: { + oid: "789", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[2]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/4", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue5: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/5", + number: 5, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue1: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/1", + number: 1, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[1].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/3/commits`, + [{ sha: commits[2].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + 404, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3/comments`, + 403, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/comments`, + { html_url: "https://github.com/successcomment-5" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 1), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 4), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 5, + "https://github.com/successcomment-5", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 5), + ); + t.true( + t.context.error.calledWith( + "Failed to add a comment to the issue/PR #%d as it doesn't exist.", + 2, + ), + ); + t.true( + t.context.error.calledWith( + "Not allowed to add a comment to the issue/PR #%d.", + 3, + ), + ); + t.true(fetch.done()); +}); + +test("Add custom comment and labels", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { + successComment: `last release: \${lastRelease.version} nextRelease: \${nextRelease.version} branch: \${branch.name} commits: \${commits.length} releases: \${releases.length} PR attribute: \${issue.prop}`, + failTitle, + releasedLabels: [ + "released on @<%= nextRelease.channel %>", + "released from <%= branch.name %>", + ], + }; + const prs = [ + { number: 1, prop: "PR prop", __typename: "PullRequest", state: "closed" }, + ]; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "2.0.0", channel: "next" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { + html_url: "https://github.com/successcomment-1", + body: `last release: ${lastRelease.version} nextRelease: ${nextRelease.version} branch: master commits: 1 releases: 1 PR attribute: PR prop`, + }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["released on @next", "released from master"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + branch: { name: "master" }, + options, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith( + "Added labels %O to PR #%d", + ["released on @next", "released from master"], + 1, + ), + ); + t.true(fetch.done()); +}); + +test("Add custom label", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: ["custom label"], failTitle }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "2.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/labels`, + {}, + { body: ["custom label"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["custom label"], 1), + ); + t.true(fetch.done()); +}); + +test("Comment on issue/PR without ading a label", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, failTitle }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "2.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); +}); + +test("Editing the release to include all release links at the bottom", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "bottom" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const releases = [ + { + name: "GitHub release", + url: "https://github.com/release", + id: releaseId, + }, + { name: "S3", url: "s3://my-bucket/release-asset" }, + { name: "Docker: docker.io/python:slim" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + body: nextRelease.notes.concat("\n---\n", getReleaseLinks(releases)), + }, + }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); +}); + +test("Editing the release to include all release links at the top", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "top" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releaseUrl = `https://github.com/${owner}/${repo}/releases/${nextRelease.version}`; + const releaseId = 1; + const releases = [ + { + name: "GitHub release", + url: "https://github.com/release", + id: releaseId, + }, + { name: "S3", url: "s3://my-bucket/release-asset" }, + { name: "Docker: docker.io/python:slim" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/releases/${releaseId}`, + { + html_url: releaseUrl, + }, + { + body: { + body: getReleaseLinks(releases) + "\n---\n" + nextRelease.notes, + }, + }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); +}); + +test("Editing the release to include all release links with no additional releases (top)", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "top" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releaseId = 1; + const releases = [ + { + name: "GitHub release", + url: "https://github.com/release", + id: releaseId, + }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); +}); + +test("Editing the release to include all release links with no additional releases (bottom)", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "bottom" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releaseId = 1; + const releases = [ + { + name: "GitHub release", + url: "https://github.com/release", + id: releaseId, + }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); +}); + +test("Editing the release to include all release links with no releases", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "bottom" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releases = []; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); -test.beforeEach(t => { - // Mock logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); }); -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); +test("Editing the release with no ID in the release", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { releasedLabels: false, addReleases: "bottom" }; + const prs = [{ number: 1, __typename: "PullRequest", state: "closed" }]; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const nextRelease = { + version: "2.0.0", + gitTag: "v1.0.0", + name: "v1.0.0", + notes: "Test release note body", + }; + const lastRelease = { version: "1.0.0" }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + { name: "S3", url: "s3://my-bucket/release-asset" }, + { name: "Docker: docker.io/python:slim" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + { html_url: "https://github.com/successcomment-1" }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + lastRelease, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 1, + "https://github.com/successcomment-1", + ), + ); + t.true(fetch.done()); }); -test.serial( - 'Add comment and labels to PRs associated with release commits and issues closed by PR/commits comments', - async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const redirectedOwner = 'test_user_2'; - const redirectedRepo = 'test_repo_2'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const prs = [ - {number: 1, pull_request: {}, state: 'closed'}, - {number: 2, pull_request: {}, body: 'Fixes #3', state: 'closed'}, - ]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [ - {hash: '123', message: 'Commit 1 message\n\n Fix #1', tree: {long: 'aaa'}}, - {hash: '456', message: 'Commit 2 message', tree: {long: 'ccc'}}, - { - hash: '789', - message: `Commit 3 message Closes https://github.com/${redirectedOwner}/${redirectedRepo}/issues/4`, - tree: {long: 'ccc'}, - }, - ]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${redirectedOwner}/${redirectedRepo}`}) - .get( - `/search/issues?q=${escape(`repo:${redirectedOwner}/${redirectedRepo}`)}+${escape('type:pr')}+${escape( - 'is:merged' - )}+${commits.map(commit => commit.hash).join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${redirectedOwner}/${redirectedRepo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .get(`/repos/${redirectedOwner}/${redirectedRepo}/pulls/2/commits`) - .reply(200, [{sha: commits[1].hash}]) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/2/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-2'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/2/labels`, '["released"]') - .reply(200, {}) - .get(`/repos/${redirectedOwner}/${redirectedRepo}/issues/3`) - .reply(200, {state: 'closed'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/3/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://github.com/successcomment-3'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/3/labels`, '["released"]') - .reply(200, {}) - .get(`/repos/${redirectedOwner}/${redirectedRepo}/issues/4`) - .reply(200, {state: 'closed'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/4/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://github.com/successcomment-4'}) - .post(`/repos/${redirectedOwner}/${redirectedRepo}/issues/4/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${redirectedOwner}/${redirectedRepo}`)}+${escape( - 'type:issue' - )}+${escape('state:open')}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 2, 'https://github.com/successcomment-2')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 2)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 3, 'https://github.com/successcomment-3')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 3)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 4, 'https://github.com/successcomment-4')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 4)); - t.true(github.isDone()); - } -); - -test.serial( - 'Add comment and labels to PRs associated with release commits and issues closed by PR/commits comments with custom URL', - async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_URL: 'https://custom-url.com', GH_TOKEN: 'github_token', GH_PREFIX: 'prefix'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const prs = [ - {number: 1, pull_request: {}, state: 'closed'}, - {number: 2, pull_request: {}, body: 'Fixes #3', state: 'closed'}, - ]; - const options = {branch: 'master', repositoryUrl: `https://custom-url.com/${owner}/${repo}.git`}; - const commits = [ - {hash: '123', message: 'Commit 1 message\n\n Fix #1'}, - {hash: '456', message: 'Commit 2 message'}, - {hash: '789', message: `Commit 3 message Closes https://custom-url.com/${owner}/${repo}/issues/4`}, - ]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://custom-url.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .get(`/repos/${owner}/${repo}/pulls/2/commits`) - .reply(200, [{sha: commits[1].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://custom-url.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://custom-url.com/successcomment-2'}) - .post(`/repos/${owner}/${repo}/issues/2/labels`, '["released"]') - .reply(200, {}) - .get(`/repos/${owner}/${repo}/issues/3`) - .reply(200, {state: 'closed'}) - .post(`/repos/${owner}/${repo}/issues/3/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://custom-url.com/successcomment-3'}) - .post(`/repos/${owner}/${repo}/issues/3/labels`, '["released"]') - .reply(200, {}) - .get(`/repos/${owner}/${repo}/issues/4`) - .reply(200, {state: 'closed'}) - .post(`/repos/${owner}/${repo}/issues/4/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://custom-url.com/successcomment-4'}) - .post(`/repos/${owner}/${repo}/issues/4/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://custom-url.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 2, 'https://custom-url.com/successcomment-2')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 2)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 3, 'https://custom-url.com/successcomment-3')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 3)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 4, 'https://custom-url.com/successcomment-4')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 4)); - t.true(github.isDone()); - } -); - -test.serial('Make multiple search queries if necessary', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; +test("Ignore errors when adding comments and closing issues", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const issues = [ + { number: 1, body: "Issue 1 body", title: failTitle }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; const prs = [ - {number: 1, pull_request: {}, state: 'closed'}, - {number: 2, pull_request: {}, state: 'closed'}, - {number: 3, pull_request: {}, state: 'closed'}, - {number: 4, pull_request: {}, state: 'closed'}, - {number: 5, pull_request: {}, state: 'closed'}, - {number: 6, pull_request: {}, state: 'closed'}, - ]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; + { number: 1, __typename: "PullRequest", state: "closed" }, + { number: 2, __typename: "PullRequest", state: "closed" }, + ]; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const commits = [ - {hash: repeat('a', 40), message: 'Commit 1 message'}, - {hash: repeat('b', 40), message: 'Commit 2 message'}, - {hash: repeat('c', 40), message: 'Commit 3 message'}, - {hash: repeat('d', 40), message: 'Commit 4 message'}, - {hash: repeat('e', 40), message: 'Commit 5 message'}, - {hash: repeat('f', 40), message: 'Commit 6 message'}, - {hash: repeat('g', 40), message: 'Commit 7 message'}, - ]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${ - commits[0].hash - }+${commits[1].hash}+${commits[2].hash}+${commits[3].hash}+${commits[4].hash}` - ) - .reply(200, {items: [prs[0], prs[1], prs[2], prs[3], prs[4]]}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${ - commits[5].hash - }+${commits[6].hash}` - ) - .reply(200, {items: [prs[5], prs[1]]}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .get(`/repos/${owner}/${repo}/pulls/2/commits`) - .reply(200, [{sha: commits[1].hash}]) - .get(`/repos/${owner}/${repo}/pulls/3/commits`) - .reply(200, [{sha: commits[2].hash}]) - .get(`/repos/${owner}/${repo}/pulls/4/commits`) - .reply(200, [{sha: commits[3].hash}]) - .get(`/repos/${owner}/${repo}/pulls/5/commits`) - .reply(200, [{sha: commits[4].hash}]) - .get(`/repos/${owner}/${repo}/pulls/6/commits`) - .reply(200, [{sha: commits[5].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-2'}) - .post(`/repos/${owner}/${repo}/issues/2/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/3/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-3'}) - .post(`/repos/${owner}/${repo}/issues/3/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/4/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-4'}) - .post(`/repos/${owner}/${repo}/issues/4/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/5/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-5'}) - .post(`/repos/${owner}/${repo}/issues/5/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/6/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-6'}) - .post(`/repos/${owner}/${repo}/issues/6/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 2, 'https://github.com/successcomment-2')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 2)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 3, 'https://github.com/successcomment-3')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 3)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 4, 'https://github.com/successcomment-4')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 4)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 5, 'https://github.com/successcomment-5')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 5)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 6, 'https://github.com/successcomment-6')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 6)); - t.true(github.isDone()); -}); + { hash: "123", message: "Commit 1 message" }, + { hash: "456", message: "Commit 2 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/1/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1/comments`, + 400, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + { html_url: "https://github.com/successcomment-2" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: issues }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2`, + 500, + { + body: { + state: "closed", + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3`, + { html_url: "https://github.com/issues/3" }, + { + body: { + state: "closed", + }, + }, + ); + + const { + errors: [error1, error2], + } = await t.throwsAsync( + success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); -test.serial( - 'Do not add comment and labels for unrelated PR returned by search (compare sha and merge_commit_sha)', - async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const prs = [{number: 1, pull_request: {}, state: 'closed'}, {number: 2, pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}, {hash: '456', message: 'Commit 2 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: 'rebased_sha'}]) - .get(`/repos/${owner}/${repo}/pulls/1`) - .reply(200, {merge_commit_sha: commits[0].hash}) - .get(`/repos/${owner}/${repo}/pulls/2/commits`) - .reply(200, [{sha: 'rebased_sha'}]) - .get(`/repos/${owner}/${repo}/pulls/2`) - .reply(200, {merge_commit_sha: 'unrelated_sha'}) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(github.isDone()); - } -); - -test.serial('Do not add comment and labels to open issues/PRs', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const prs = [{number: 1, pull_request: {}, body: 'Fixes #2', state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .get(`/repos/${owner}/${repo}/issues/2`) - .reply(200, {state: 'open'}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(t.context.log.calledWith("Skip comment and labels on issue #%d as it's open: %s", 2)); - t.true(github.isDone()); + t.is(error1.status, 400); + t.is(error2.status, 500); + t.true( + t.context.error.calledWith( + "Failed to add a comment to the issue/PR #%d.", + 1, + ), + ); + t.true(t.context.error.calledWith("Failed to close the issue #%d.", 2)); + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 2, + "https://github.com/successcomment-2", + ), + ); + t.true( + t.context.log.calledWith( + "Closed issue #%d: %s.", + 3, + "https://github.com/issues/3", + ), + ); + t.true(fetch.done()); }); -test.serial('Do not add comment and labels if no PR is associated with release commits', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: []}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(github.isDone()); +test("Close open issues when a release is successful", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle }; + const issues = [ + { number: 1, body: "Issue 1 body", title: failTitle }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getAssociatedPRs\(/); + return true; + }, + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2`, + { html_url: "https://github.com/issues/2" }, + { + body: { + state: "closed", + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3`, + { html_url: "https://github.com/issues/3" }, + { + body: { + state: "closed", + }, + }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Closed issue #%d: %s.", + 2, + "https://github.com/issues/2", + ), + ); + t.true( + t.context.log.calledWith( + "Closed issue #%d: %s.", + 3, + "https://github.com/issues/3", + ), + ); + t.true(fetch.done()); }); -test.serial('Do not add comment and labels to PR/issues from other repo', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test('Skip comment on on issues/PR if "successComment" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle, successComment: false }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const commits = [ - {hash: '123', message: 'Commit 1 message\n\n Fix other/other#1'}, - {hash: '456', message: `Commit 2 message Fix ${owner}/${repo}#2`}, - {hash: '789', message: 'Commit 3 message Closes other/other#3'}, - ]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: []}) - .get(`/repos/${owner}/${repo}/issues/2`) - .reply(200, {state: 'closed'}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://github.com/successcomment-2'}) - .post(`/repos/${owner}/${repo}/issues/2/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 2, 'https://github.com/successcomment-2')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 2)); - t.true(github.isDone()); + { + hash: "123", + message: "Commit 1 message\n\n Fix #1", + tree: { long: "aaa" }, + }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith("Skip commenting on issues and pull requests."), + ); + t.true(fetch.done()); }); -test.serial('Ignore missing and forbidden issues/PRs', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; - const prs = [ - {number: 1, pull_request: {}, state: 'closed'}, - {number: 2, pull_request: {}, body: 'Fixes #4', state: 'closed'}, - {number: 3, pull_request: {}, body: 'Fixes #5', state: 'closed'}, - ]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; +test('Does not comment/label on issues/PR if "successCommentCondition" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { failTitle, successCommentCondition: false }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; const commits = [ - {hash: '123', message: 'Commit 1 message\n\n Fix #1'}, - {hash: '456', message: 'Commit 2 message'}, - {hash: '789', message: 'Commit 3 message'}, - ]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .get(`/repos/${owner}/${repo}/pulls/2/commits`) - .reply(200, [{sha: commits[1].hash}]) - .get(`/repos/${owner}/${repo}/pulls/3/commits`) - .reply(200, [{sha: commits[2].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, {body: /This PR is included/}) - .times(3) - .reply(404) - .post(`/repos/${owner}/${repo}/issues/3/comments`, {body: /This PR is included/}) - .reply(403) - .get(`/repos/${owner}/${repo}/issues/4`) - .reply(200, {state: 'closed'}) - .get(`/repos/${owner}/${repo}/issues/5`) - .reply(200, {state: 'closed'}) - .post(`/repos/${owner}/${repo}/issues/4/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://github.com/successcomment-4'}) - .post(`/repos/${owner}/${repo}/issues/4/labels`, '["released"]') - .reply(200, {}) - .post(`/repos/${owner}/${repo}/issues/5/comments`, {body: /This issue has been resolved/}) - .reply(200, {html_url: 'https://github.com/successcomment-5'}) - .post(`/repos/${owner}/${repo}/issues/5/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 4, 'https://github.com/successcomment-4')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 4)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 5, 'https://github.com/successcomment-5')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 5)); - t.true(t.context.error.calledWith("Failed to add a comment to the issue #%d as it doesn't exist.", 2)); - t.true(t.context.error.calledWith('Not allowed to add a comment to the issue #%d.', 3)); - t.true(github.isDone()); + { + hash: "123", + message: "Commit 1 message\n\n Fix #1", + tree: { long: "aaa" }, + }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: [] }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith("Skip commenting on issues and pull requests."), + ); + t.true(fetch.done()); }); -test.serial('Add custom comment', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; +test('Add comment and label to found issues/associatedPR using the "successCommentCondition": if specific label is found', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; const pluginConfig = { - successComment: `last release: \${lastRelease.version} nextRelease: \${nextRelease.version} branch: \${branch} commits: \${commits.length} releases: \${releases.length} PR attribute: \${issue.prop}`, failTitle, + // Issues with the label "semantic-release-relevant" will be commented and labeled + successCommentCondition: + "<% return issue.labels?.some((label) => { return label.name === ('semantic-release-relevant'); }); %>", }; - const prs = [{number: 1, prop: 'PR prop', pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const lastRelease = {version: '1.0.0'}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '2.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, { - body: /last release: 1\.0\.0 nextRelease: 2\.0\.0 branch: master commits: 1 releases: 1 PR attribute: PR prop/, - }) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["released"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, lastRelease, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['released'], 1)); - t.true(github.isDone()); -}); + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message" }, + { hash: "456", message: "Commit 2 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + const issues = [ + { number: 1, body: "Issue 1 body", title: failTitle }, + { number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle }, + { number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; -test.serial('Add custom label', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {releasedLabels: ['custom label'], failTitle}; - const prs = [{number: 1, pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const lastRelease = {version: '1.0.0'}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '2.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .post(`/repos/${owner}/${repo}/issues/1/labels`, '["custom label"]') - .reply(200, {}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, lastRelease, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(t.context.log.calledWith('Added labels %O to issue #%d', ['custom label'], 1)); - t.true(github.isDone()); -}); + const prs = [ + { + __typename: "PullRequest", + id: "PR_kwDOMLlZj85z_R2M", + title: "fix: will semantic-release recognize the associated issue ", + body: "", + url: "https://github.com/babblebey/sr-github/pull/12", + number: 5, + createdAt: "2024-06-30T14:43:48Z", + updatedAt: "2024-08-26T16:19:57Z", + closedAt: "2024-06-30T14:44:05Z", + comments: { + totalCount: 12, + }, + state: "MERGED", + author: { + login: "babblebey", + url: "https://github.com/babblebey", + avatarUrl: + "https://avatars.githubusercontent.com/u/25631971?u=f4597764b2c31478a516d97bb9ecd019b5e62ae7&v=4", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "LA_kwDOMLlZj88AAAABp9kjcQ", + url: "https://github.com/babblebey/sr-github/labels/released", + name: "semantic-release-relevant", + color: "ededed", + description: "This issue is relevant to semantic-release", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + mergeable: "UNKNOWN", + changedFiles: 1, + mergedAt: "2024-06-30T14:44:05Z", + isDraft: false, + mergedBy: { + login: "babblebey", + avatarUrl: + "https://avatars.githubusercontent.com/u/25631971?u=f4597764b2c31478a516d97bb9ecd019b5e62ae7&v=4", + url: "https://github.com/babblebey", + }, + commits: { + totalCount: 1, + }, + }, + { + __typename: "PullRequest", + id: "PR_kwDOMLlZj85z_R2M", + title: "fix: will semantic-release recognize the associated issue ", + body: "", + url: "https://github.com/babblebey/sr-github/pull/12", + number: 4, + createdAt: "2024-06-30T14:43:48Z", + updatedAt: "2024-08-26T16:19:57Z", + closedAt: "2024-06-30T14:44:05Z", + comments: { + totalCount: 12, + }, + state: "MERGED", + author: { + login: "babblebey", + url: "https://github.com/babblebey", + avatarUrl: + "https://avatars.githubusercontent.com/u/25631971?u=f4597764b2c31478a516d97bb9ecd019b5e62ae7&v=4", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "LA_kwDOMLlZj88AAAABp9kjcQ", + url: "https://github.com/babblebey/sr-github/labels/released", + name: "released", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + mergeable: "UNKNOWN", + changedFiles: 1, + mergedAt: "2024-06-30T14:44:05Z", + isDraft: false, + mergedBy: { + login: "babblebey", + avatarUrl: + "https://avatars.githubusercontent.com/u/25631971?u=f4597764b2c31478a516d97bb9ecd019b5e62ae7&v=4", + url: "https://github.com/babblebey", + }, + commits: { + totalCount: 1, + }, + }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/4/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/5/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getSRIssues("), + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: issues }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/comments`, + { html_url: "https://github.com/successcomment-5" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/5/labels`, + {}, + { body: ["released"] }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2`, + { html_url: "https://github.com/issues/2" }, + { + body: { + state: "closed", + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/3`, + { html_url: "https://github.com/issues/3" }, + { + body: { + state: "closed", + }, + }, + ); -test.serial('Comment on issue/PR without ading a label', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {releasedLabels: false, failTitle}; - const prs = [{number: 1, pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const lastRelease = {version: '1.0.0'}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '2.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-1'}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, lastRelease, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 1, 'https://github.com/successcomment-1')); - t.true(github.isDone()); + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + + t.true( + t.context.log.calledWith( + "Added comment to PR #%d: %s", + 5, + "https://github.com/successcomment-5", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to PR #%d", ["released"], 5), + ); + t.true(fetch.done()); }); -test.serial('Ignore errors when adding comments and closing issues', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; +test('Does not comment/label associatedPR and relatedIssues created by "Bots"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { + failTitle, + // Only issues or PRs not created by "Bot" will be commented and labeled + successCommentCondition: "<% return issue.user.type !== 'Bot'; %>", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message" }, + { hash: "456", message: "Commit 2 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; const issues = [ - {number: 1, body: 'Issue 1 body', title: failTitle}, - {number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle}, - {number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle}, - ]; - const prs = [{number: 1, pull_request: {}, state: 'closed'}, {number: 2, pull_request: {}, state: 'closed'}]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}, {hash: '456', message: 'Commit 2 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: prs}) - .get(`/repos/${owner}/${repo}/pulls/1/commits`) - .reply(200, [{sha: commits[0].hash}]) - .get(`/repos/${owner}/${repo}/pulls/2/commits`) - .reply(200, [{sha: commits[1].hash}]) - .post(`/repos/${owner}/${repo}/issues/1/comments`, {body: /This PR is included/}) - .reply(400, {}) - .post(`/repos/${owner}/${repo}/issues/2/comments`, {body: /This PR is included/}) - .reply(200, {html_url: 'https://github.com/successcomment-2'}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: issues}) - .patch(`/repos/${owner}/${repo}/issues/2`, {state: 'closed'}) - .times(4) - .reply(500) - .patch(`/repos/${owner}/${repo}/issues/3`, {state: 'closed'}) - .reply(200, {html_url: 'https://github.com/issues/3'}); - - const [error1, error2] = await t.throwsAsync( - success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}) + { number: 1, body: `Issue 1 body\n\n${ISSUE_ID}`, title: failTitle }, + ]; + const prs = [ + { + __typename: "PullRequest", + number: 2, + id: "PR_kwDOMLlZj851SZzc", + title: "pr title", + body: "Fixes #4", + url: "https://pr-url", + createdAt: "2024-07-13T09:57:51Z", + updatedAt: "2024-08-29T12:15:33Z", + closedAt: "2024-07-13T09:58:50Z", + comments: { + totalCount: 23, + }, + state: "MERGED", + author: { + login: "user_login", + url: "https://user-url", + avatarUrl: "https://avatar-url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + mergeable: "UNKNOWN", + changedFiles: 1, + mergedAt: "2024-07-13T09:58:50Z", + isDraft: false, + mergedBy: { + login: "user", + avatarUrl: "https://alink-to-avatar", + url: "https://user-url", + }, + commits: { + totalCount: 1, + }, + }, + { + __typename: "PullRequest", + number: 3, + id: "PR_kwDOMLlZj851SZzc", + title: "pr title", + body: "Fixes #5", + url: "https://pr-url", + createdAt: "2024-07-13T09:57:51Z", + updatedAt: "2024-08-29T12:15:33Z", + closedAt: "2024-07-13T09:58:50Z", + comments: { + totalCount: 23, + }, + state: "MERGED", + author: { + login: "user_login", + url: "https://user-url", + avatarUrl: "https://avatar-url", + __typename: "Bot", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + mergeable: "UNKNOWN", + changedFiles: 1, + mergedAt: "2024-07-13T09:58:50Z", + isDraft: false, + mergedBy: { + login: "user", + avatarUrl: "https://alink-to-avatar", + url: "https://user-url", + }, + commits: { + totalCount: 1, + }, + }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/4", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + issue5: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/5", + number: 5, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "Bot", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/3/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/comments`, + { html_url: "https://github.com/successcomment-2" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/2/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => { + t.is(url, "https://api.github.local/graphql"); + t.regex(JSON.parse(body).query, /query getSRIssues\(/); + return true; + }, + { + data: { + repository: { + issues: { nodes: [] }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/search/issues?q=${encodeURIComponent( + "in:title", + )}+${encodeURIComponent(`repo:${owner}/${repo}`)}+${encodeURIComponent( + "type:issue", + )}+${encodeURIComponent("state:open")}+${encodeURIComponent(failTitle)}`, + { items: issues }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1`, + { html_url: "https://github.com/issues/1" }, + { + body: { + state: "closed", + }, + }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, ); - t.is(error1.status, 400); - t.is(error2.status, 500); - t.true(t.context.error.calledWith('Failed to add a comment to the issue #%d.', 1)); - t.true(t.context.error.calledWith('Failed to close the issue #%d.', 2)); - t.true(t.context.log.calledWith('Added comment to issue #%d: %s', 2, 'https://github.com/successcomment-2')); - t.true(t.context.log.calledWith('Closed issue #%d: %s.', 3, 'https://github.com/issues/3')); - t.true(github.isDone()); + t.true( + t.context.log.calledWith( + "Closed issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true(t.context.log.calledWith("Skip commenting on PR #%d.", 3)); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 4), + ); + t.true(fetch.done()); }); -test.serial('Close open issues when a release is successful', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle}; +test('Does not comment/label "associatedPR" when "successCommentCondition" disables it: Only comment on "relatedIssues"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const failTitle = "The automated release is failing 🚨"; + const pluginConfig = { + failTitle, + // Only issues will be commented and labeled (not PRs) + successCommentCondition: "<% return !issue.pull_request; %>", + }; + const options = { + repositoryUrl: `https://github.com/${owner}/${repo}.git`, + }; + const commits = [ + { hash: "123", message: "Commit 1 message" }, + { hash: "456", message: "Commit 2 message" }, + ]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; const issues = [ - {number: 1, body: 'Issue 1 body', title: failTitle}, - {number: 2, body: `Issue 2 body\n\n${ISSUE_ID}`, title: failTitle}, - {number: 3, body: `Issue 3 body\n\n${ISSUE_ID}`, title: failTitle}, - ]; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: []}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: issues}) - .patch(`/repos/${owner}/${repo}/issues/2`, {state: 'closed'}) - .reply(200, {html_url: 'https://github.com/issues/2'}) - .patch(`/repos/${owner}/${repo}/issues/3`, {state: 'closed'}) - .reply(200, {html_url: 'https://github.com/issues/3'}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Closed issue #%d: %s.', 2, 'https://github.com/issues/2')); - t.true(t.context.log.calledWith('Closed issue #%d: %s.', 3, 'https://github.com/issues/3')); - t.true(github.isDone()); -}); + { number: 1, body: `Issue 1 body\n\n${ISSUE_ID}`, title: failTitle }, + { + number: 4, + body: `Issue 4 body`, + title: "Issue 4 title", + state: "closed", + }, + ]; + const prs = [ + { number: 2, __typename: "PullRequest", body: "Fixes #4", state: "closed" }, + { number: 3, __typename: "PullRequest", state: "closed" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + }) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getAssociatedPRs("), + { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[0]], + }, + }, + commit456: { + oid: "456", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [prs[1]], + }, + }, + }, + }, + }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getRelatedIssues("), + { + data: { + repository: { + issue4: { + __typename: "Issue", + id: "I_kw", + title: "issue title", + body: "", + url: "https://github.com/owner/repo/issues/4", + number: 4, + createdAt: "2024-07-13T09:58:09Z", + updatedAt: "2024-08-26T16:19:59Z", + closedAt: "2024-07-13T09:58:51Z", + comments: { + totalCount: 12, + }, + state: "CLOSED", + author: { + login: "user", + url: "author_url", + avatarUrl: "author_avatar_url", + __typename: "User", + }, + authorAssociation: "OWNER", + activeLockReason: null, + labels: { + nodes: [ + { + id: "label_id", + url: "label_url", + name: "label_name", + color: "ededed", + description: "this is a label description", + isDefault: false, + }, + ], + }, + milestone: null, + locked: false, + }, + }, + }, + }, + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/2/commits`, + [{ sha: commits[0].hash }], + ) + .getOnce( + `https://api.github.local/repos/${owner}/${repo}/pulls/3/commits`, + [{ sha: commits[1].hash }], + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/comments`, + { html_url: "https://github.com/successcomment-4" }, + ) + .postOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/4/labels`, + {}, + { body: ["released"] }, + ) + .postOnce( + (url, { body }) => + url === "https://api.github.local/graphql" && + JSON.parse(body).query.includes("query getSRIssues("), + { + data: { + repository: { + issues: { nodes: issues }, + }, + }, + }, + ) + .patchOnce( + `https://api.github.local/repos/${owner}/${repo}/issues/1`, + { html_url: "https://github.com/issues/1" }, + { + body: { + state: "closed", + }, + }, + ); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); -test.serial('Skip commention on issues/PR if "successComment" is "false"', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const failTitle = 'The automated release is failing 🚨'; - const pluginConfig = {failTitle, successComment: false}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message\n\n Fix #1', tree: {long: 'aaa'}}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape('in:title')}+${escape(`repo:${owner}/${repo}`)}+${escape('type:issue')}+${escape( - 'state:open' - )}+${escape(failTitle)}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - - t.true(t.context.log.calledWith('Skip commenting on issues and pull requests.')); - t.true(github.isDone()); + t.true( + t.context.log.calledWith( + "Closed issue #%d: %s.", + 1, + "https://github.com/issues/1", + ), + ); + t.true( + t.context.log.calledWith( + "Added comment to issue #%d: %s", + 4, + "https://github.com/successcomment-4", + ), + ); + t.true( + t.context.log.calledWith("Added labels %O to issue #%d", ["released"], 4), + ); + t.true(fetch.done()); }); -test.serial('Skip closing issues if "failComment" is "false"', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const pluginConfig = {failComment: false}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - t.true(t.context.log.calledWith('Skip closing issue.')); - t.true(github.isDone()); +test('Skip closing issues if "failComment" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failComment: false }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + }, + }, + }); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + t.true(t.context.log.calledWith("Skip closing issue.")); + t.true(fetch.done()); }); -test.serial('Skip closing issues if "failTitle" is "false"', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'github_token'}; - const pluginConfig = {failTitle: false}; - const options = {branch: 'master', repositoryUrl: `https://github.com/${owner}/${repo}.git`}; - const commits = [{hash: '123', message: 'Commit 1 message'}]; - const nextRelease = {version: '1.0.0'}; - const releases = [{name: 'GitHub release', url: 'https://github.com/release'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {full_name: `${owner}/${repo}`}) - .get( - `/search/issues?q=${escape(`repo:${owner}/${repo}`)}+${escape('type:pr')}+${escape('is:merged')}+${commits - .map(commit => commit.hash) - .join('+')}` - ) - .reply(200, {items: []}); - - await success(pluginConfig, {env, options, commits, nextRelease, releases, logger: t.context.logger}); - t.true(t.context.log.calledWith('Skip closing issue.')); - t.true(github.isDone()); +test('Skip closing issues if "failTitle" is "false"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GITHUB_TOKEN: "github_token" }; + const pluginConfig = { failTitle: false }; + const options = { repositoryUrl: `https://github.com/${owner}/${repo}.git` }; + const commits = [{ hash: "123", message: "Commit 1 message" }]; + const nextRelease = { version: "1.0.0" }; + const releases = [ + { name: "GitHub release", url: "https://github.com/release" }, + ]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + full_name: `${owner}/${repo}`, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .postOnce("https://api.github.local/graphql", { + data: { + repository: { + commit123: { + oid: "123", + associatedPullRequests: { + pageInfo: { + endCursor: "NI", + hasNextPage: false, + }, + nodes: [], + }, + }, + }, + }, + }); + + await success( + pluginConfig, + { + env, + options, + branch: { name: "master" }, + commits, + nextRelease, + releases, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ); + t.true(t.context.log.calledWith("Skip closing issue.")); + t.true(fetch.done()); }); diff --git a/test/to-octokit-options.test.js b/test/to-octokit-options.test.js new file mode 100644 index 00000000..38c3a4b8 --- /dev/null +++ b/test/to-octokit-options.test.js @@ -0,0 +1,69 @@ +import { createServer as _createServer } from "node:https"; + +import test from "ava"; +import { HttpProxyAgent } from "http-proxy-agent"; +import { HttpsProxyAgent } from "https-proxy-agent"; + +import { toOctokitOptions } from "../lib/octokit.js"; + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; + +test("Use a http proxy", async (t) => { + const options = toOctokitOptions({ + githubToken: "github_token", + githubUrl: `http://localhost:10001`, + githubApiPathPrefix: "", + proxy: `http://localhost:1002`, + }); + const { request, ...rest } = options; + t.deepEqual(rest, { + baseUrl: `http://localhost:10001`, + auth: "github_token", + }); + t.true(request.agent instanceof HttpProxyAgent); +}); + +test("Use a https proxy", async (t) => { + const options = toOctokitOptions({ + githubToken: "github_token", + githubUrl: `https://localhost:10001`, + githubApiPathPrefix: "", + proxy: `https://localhost:1002`, + }); + const { request, ...rest } = options; + t.deepEqual(rest, { + baseUrl: `https://localhost:10001`, + auth: "github_token", + }); + t.true(request.agent instanceof HttpsProxyAgent); +}); + +test("Do not use a proxy if set to false", async (t) => { + const options = toOctokitOptions({ + githubToken: "github_token", + githubUrl: `http://localhost:10001`, + githubApiPathPrefix: "", + proxy: false, + }); + const { request, ...rest } = options; + t.deepEqual(rest, { + baseUrl: `http://localhost:10001`, + auth: "github_token", + }); + t.is(request.agent, undefined); +}); + +test("githubUrl with trailing slash", async (t) => { + const options = toOctokitOptions({ + githubUrl: "http://localhost:10001/", + githubApiPathPrefix: "", + }); + t.is(options.baseUrl, "http://localhost:10001"); +}); + +test("githubApiUrl with trailing slash", async (t) => { + const options = toOctokitOptions({ + githubApiUrl: "http://api.localhost:10001/", + }); + t.is(options.baseUrl, "http://api.localhost:10001"); +}); diff --git a/test/verify.test.js b/test/verify.test.js index 75f4c3c3..d5154c36 100644 --- a/test/verify.test.js +++ b/test/verify.test.js @@ -1,788 +1,2724 @@ -import test from 'ava'; -import nock from 'nock'; -import {stub} from 'sinon'; -import proxyquire from 'proxyquire'; -import {authenticate} from './helpers/mock-github'; -import rateLimit from './helpers/rate-limit'; +import sinon from "sinon"; +import test from "ava"; +import fetchMock from "fetch-mock"; + +import { TestOctokit } from "./helpers/test-octokit.js"; /* eslint camelcase: ["error", {properties: "never"}] */ -const verify = proxyquire('../lib/verify', { - './get-client': proxyquire('../lib/get-client', {'./definitions/rate-limit': rateLimit}), -}); +import verify from "../lib/verify.js"; -test.beforeEach(t => { +test.beforeEach((t) => { // Mock logger - t.context.log = stub(); - t.context.error = stub(); - t.context.logger = {log: t.context.log, error: t.context.error}; -}); - -test.afterEach.always(() => { - // Clear nock - nock.cleanAll(); -}); - -test.serial('Verify package, token and repository access', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const proxy = 'https://localhost'; - const assets = [{path: 'lib/file.js'}, 'file.js']; - const successComment = 'Test comment'; - const failTitle = 'Test title'; - const failComment = 'Test comment'; - const labels = ['semantic-release']; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + t.context.log = sinon.stub(); + t.context.error = sinon.stub(); + t.context.warn = sinon.stub(); + t.context.logger = { + log: t.context.log, + error: t.context.error, + warn: t.context.warn, + }; +}); + +test("Verify package, token and repository access", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const proxy = "https://localhost"; + const assets = [{ path: "lib/file.js" }, "file.js"]; + const successComment = "Test comment"; + const failTitle = "Test title"; + const failComment = "Test comment"; + const labels = ["semantic-release"]; + const discussionCategoryName = "Announcements"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {proxy, assets, successComment, failTitle, failComment, labels}, - {env, options: {repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`}, logger: t.context.logger} - ) - ); - t.true(github.isDone()); -}); - -test.serial( - 'Verify package, token and repository access with "proxy", "asset", "successComment", "failTitle", "failComment" and "label" set to "null"', - async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const proxy = null; - const assets = null; - const successComment = null; - const failTitle = null; - const failComment = null; - const labels = null; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); - - await t.notThrowsAsync( - verify( - {proxy, assets, successComment, failTitle, failComment, labels}, - {env, options: {repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`}, logger: t.context.logger} - ) - ); - t.true(github.isDone()); - } -); - -test.serial('Verify package, token and repository access and custom URL with prefix', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const githubUrl = 'https://othertesturl.com:9090'; - const githubApiPathPrefix = 'prefix'; - const github = authenticate(env, {githubUrl, githubApiPathPrefix}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + { + proxy, + assets, + successComment, + failTitle, + failComment, + labels, + discussionCategoryName, + }, + { + env, + options: { + repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + t.true(fetch.done()); +}); + +test('Verify package, token and repository access with "proxy", "asset", "discussionCategoryName", "successComment", "failTitle", "failComment" and "label" set to "null"', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const proxy = null; + const assets = null; + const successComment = null; + const failTitle = null; + const failComment = null; + const labels = null; + const discussionCategoryName = null; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { + proxy, + assets, + successComment, + failTitle, + failComment, + labels, + discussionCategoryName, + }, + { + env, + options: { + repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + t.true(fetch.done()); +}); + +test("Verify package, token and repository access and custom URL with prefix", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const githubUrl = "https://othertesturl.com:9090"; + const githubApiPathPrefix = "prefix"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/prefix/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {githubUrl, githubApiPathPrefix}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { githubUrl, githubApiPathPrefix }, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication (%s)', 'https://othertesturl.com:9090/prefix']); + t.true(fetch.done()); + t.deepEqual(t.context.log.args[0], [ + "Verify GitHub authentication (%s)", + "https://othertesturl.com:9090/prefix", + ]); }); -test.serial('Verify package, token and repository access and custom URL without prefix', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const githubUrl = 'https://othertesturl.com:9090'; - const github = authenticate(env, {githubUrl}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test("Verify package, token and repository access and custom URL without prefix", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const githubUrl = "https://othertesturl.com:9090"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {githubUrl}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { githubUrl }, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication (%s)', 'https://othertesturl.com:9090']); + t.true(fetch.done()); + t.deepEqual(t.context.log.args[0], [ + "Verify GitHub authentication (%s)", + "https://othertesturl.com:9090", + ]); }); -test.serial('Verify package, token and repository access and shorthand repositoryUrl URL', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const githubUrl = 'https://othertesturl.com:9090'; - const github = authenticate(env, {githubUrl}) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test("Verify package, token and repository access and shorthand repositoryUrl URL", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const githubUrl = "https://othertesturl.com:9090"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:9090/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( - verify({githubUrl}, {env, options: {repositoryUrl: `github:${owner}/${repo}`}, logger: t.context.logger}) + verify( + { githubUrl }, + { + env, + options: { repositoryUrl: `github:${owner}/${repo}` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication (%s)', 'https://othertesturl.com:9090']); + t.true(fetch.done()); + t.deepEqual(t.context.log.args[0], [ + "Verify GitHub authentication (%s)", + "https://othertesturl.com:9090", + ]); }); -test.serial('Verify package, token and repository with environment variables', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; +test("Verify package, token and repository with environment variables", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; const env = { - GH_URL: 'https://othertesturl.com:443', - GH_TOKEN: 'github_token', - GH_PREFIX: 'prefix', - HTTP_PROXY: 'https://localhost', + GH_URL: "https://othertesturl.com:443", + GH_TOKEN: "github_token", + GH_PREFIX: "prefix", + HTTP_PROXY: "https://localhost", }; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( - verify({}, {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger}) + verify( + {}, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); - t.deepEqual(t.context.log.args[0], ['Verify GitHub authentication (%s)', 'https://othertesturl.com:443/prefix']); + t.true(fetch.done()); + t.deepEqual(t.context.log.args[0], [ + "Verify GitHub authentication (%s)", + "https://othertesturl.com:443/prefix", + ]); }); -test.serial('Verify package, token and repository access with alternative environment varialbes', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; +test("Verify package, token and repository access with alternative environment variables", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; const env = { - GITHUB_URL: 'https://othertesturl.com:443', - GITHUB_TOKEN: 'github_token', - GITHUB_PREFIX: 'prefix', + GITHUB_URL: "https://othertesturl.com:443", + GITHUB_TOKEN: "github_token", + GITHUB_PREFIX: "prefix", }; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); + + const fetch = fetchMock + .sandbox() + .getOnce(`https://othertesturl.com:443/prefix/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( - verify({}, {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger}) + verify( + {}, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "proxy" is a String', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const proxy = 'https://locahost'; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test("Verify package, token and repository access with custom API URL", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const githubUrl = "https://othertesturl.com:9090"; + const githubApiUrl = "https://api.othertesturl.com:9090"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.othertesturl.com:9090/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {proxy}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { githubUrl, githubApiUrl }, + { + env, + options: { repositoryUrl: `github:${owner}/${repo}` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); + t.deepEqual(t.context.log.args[0], [ + "Verify GitHub authentication (%s)", + "https://api.othertesturl.com:9090", + ]); }); -test.serial('Verify "proxy" is an object with "host" and "port" properties', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const proxy = {host: 'locahost', port: 80}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test("Verify package, token and repository access with API URL in environment variable", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GITHUB_URL: "https://othertesturl.com:443", + GITHUB_API_URL: "https://api.othertesturl.com:443", + GITHUB_TOKEN: "github_token", + }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.othertesturl.com:443/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `htttps://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + t.true(fetch.done()); +}); + +test('Verify "proxy" is a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const proxy = "https://locahost"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {proxy}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { proxy }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assets" is a String', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assets = 'file2.js'; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "proxy" is an object with "host" and "port" properties', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const proxy = { host: "locahost", port: 80 }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assets}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { proxy }, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assets" is an Object with a path property', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assets = {path: 'file2.js'}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "proxy" is a Boolean set to false', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const proxy = false; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assets}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { proxy }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assets" is an Array of Object with a path property', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assets = [{path: 'file1.js'}, {path: 'file2.js'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "assets" is a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = "file2.js"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assets}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assets" is an Array of glob Arrays', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assets = [['dist/**', '!**/*.js'], 'file2.js']; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "assets" is an Object with a path property', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = { path: "file2.js" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assets}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assets" is an Array of Object with a glob Arrays in path property', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assets = [{path: ['dist/**', '!**/*.js']}, {path: 'file2.js'}]; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "assets" is an Array of Object with a path property', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = [{ path: "file1.js" }, { path: "file2.js" }]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assets}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "labels" is a String', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const labels = 'semantic-release'; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "assets" is an Array of glob Arrays', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = [["dist/**", "!**/*.js"], "file2.js"]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {labels}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); }); -test.serial('Verify "assignees" is a String', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const assignees = 'user'; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: true}}); +test('Verify "assets" is an Array of Object with a glob Arrays in path property', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = [{ path: ["dist/**", "!**/*.js"] }, { path: "file2.js" }]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {assignees}, - {env, options: {repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { + repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.true(github.isDone()); + t.true(fetch.done()); +}); + +test('Verify "labels" is a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const labels = "semantic-release"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { labels }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "assignees" is a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assignees = "user"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { assignees }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "addReleases" is a valid string (top)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = "top"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { addReleases }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "addReleases" is a valid string (bottom)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = "bottom"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { addReleases }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "addReleases" is valid (false)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = false; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { addReleases }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "draftRelease" is valid (true)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const draftRelease = true; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { draftRelease }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); +}); + +test('Verify "draftRelease" is valid (false)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const draftRelease = false; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + await t.notThrowsAsync( + verify( + { draftRelease }, + { + env, + options: { repositoryUrl: `git@othertesturl.com:${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); }); // https://github.com/semantic-release/github/issues/182 -test.serial('Verify if run in GitHub Action', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GITHUB_TOKEN: 'v1.1234567890123456789012345678901234567890', GITHUB_ACTION: 'Release'}; - const proxy = 'https://localhost'; - const assets = [{path: 'lib/file.js'}, 'file.js']; - const successComment = 'Test comment'; - const failTitle = 'Test title'; - const failComment = 'Test comment'; - const labels = ['semantic-release']; +test("Verify if run in GitHub Action", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GITHUB_TOKEN: "v1.1234567890123456789012345678901234567890", + GITHUB_ACTION: "Release", + }; + const proxy = "https://localhost"; + const assets = [{ path: "lib/file.js" }, "file.js"]; + const successComment = "Test comment"; + const failTitle = "Test title"; + const failComment = "Test comment"; + const labels = ["semantic-release"]; + const discussionCategoryName = "Announcements"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); await t.notThrowsAsync( verify( - {proxy, assets, successComment, failTitle, failComment, labels}, - {env, options: {repositoryUrl: `git+https://othertesturl.com/${owner}/${repo}.git`}, logger: t.context.logger} - ) + { proxy, assets, successComment, failTitle, failComment, labels }, + { + env, + options: { + repositoryUrl: `git+https://othertesturl.com:9090/${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); + + t.true(fetch.done()); }); -test('Throw SemanticReleaseError for missing github token', async t => { - const [error] = await t.throwsAsync( +// https://github.com/semantic-release/github/issues/182 +test("Verify if run in GitHub Action and repo is renamed", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { + GITHUB_TOKEN: "v1.1234567890123456789012345678901234567890", + GITHUB_ACTION: "Release", + }; + const proxy = "https://localhost"; + const assets = [{ path: "lib/file.js" }, "file.js"]; + const successComment = "Test comment"; + const failTitle = "Test title"; + const failComment = "Test comment"; + const labels = ["semantic-release"]; + const discussionCategoryName = "Announcements"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + clone_url: `https://api.github.local/${owner}/${repo}2.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { proxy, assets, successComment, failTitle, failComment, labels }, + { + env, + options: { + repositoryUrl: `git+https://othertesturl.com:9090/${owner}/${repo}.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); +}); + +test("Verify if token is a Github installation token and repo is renamed", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + clone_url: `https://api.github.local/${owner}/${repo}2.git`, + }) + .headOnce( + "https://api.github.local/installation/repositories?per_page=1", + 200, + ); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); +}); + +test("Throw SemanticReleaseError for missing github token", async (t) => { + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( {}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { + env: {}, + options: { + repositoryUrl: "https://github.com/semantic-release/github.git", + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'ENOGHTOKEN'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "ENOGHTOKEN"); }); -test.serial('Throw SemanticReleaseError for invalid token', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(401); +test("Throw SemanticReleaseError for invalid token", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, 401); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDGHTOKEN"); + t.true(fetch.done()); +}); + +test("Throw SemanticReleaseError for invalid repositoryUrl", async (t) => { + const env = { GH_TOKEN: "github_token" }; + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: "invalid_url" }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDGITHUBURL"); +}); + +test("Throw SemanticReleaseError if token doesn't have the push permission on the repository and it's not a Github installation token", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .headOnce( + "https://api.github.local/installation/repositories?per_page=1", + 403, + ); - const [error] = await t.throwsAsync( - verify({}, {env, options: {repositoryUrl: `https://github.com/${owner}/${repo}.git`}, logger: t.context.logger}) + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDGHTOKEN'); - t.true(github.isDone()); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EGHNOPERMISSION"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError for invalid repositoryUrl', async t => { - const env = {GH_TOKEN: 'github_token'}; +test("Do not throw SemanticReleaseError if token doesn't have the push permission but it is a Github installation token", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: false, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }) + .headOnce( + "https://api.github.local/installation/repositories?per_page=1", + 200, + ); - const [error] = await t.throwsAsync( - verify({}, {env, options: {repositoryUrl: 'invalid_url'}, logger: t.context.logger}) + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDGITHUBURL'); + t.true(fetch.done()); }); -test.serial("Throw SemanticReleaseError if token doesn't have the push permission on the repository", async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(200, {permissions: {push: false}}); +test("Throw SemanticReleaseError if the repository doesn't exist", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; - const [error] = await t.throwsAsync( - verify({}, {env, options: {repositoryUrl: `https://github.com/${owner}/${repo}.git`}, logger: t.context.logger}) + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, 404); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EGHNOPERMISSION'); - t.true(github.isDone()); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISSINGREPO"); + t.true(fetch.done()); }); -test.serial("Throw SemanticReleaseError if the repository doesn't exist", async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .times(4) - .reply(404); +test(`Don't throw an error if owner/repo only differs in case`, async (t) => { + const env = { GH_TOKEN: "github_token" }; - const [error] = await t.throwsAsync( - verify({}, {env, options: {repositoryUrl: `https://github.com/${owner}/${repo}.git`}, logger: t.context.logger}) + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/org/foo`, + { + permissions: { push: true }, + clone_url: `https://github.com/ORG/FOO.git`, + }, + { repeat: 2 }, ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EMISSINGREPO'); - t.true(github.isDone()); + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { + repositoryUrl: `https://github.com/org/foo.git`, + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); }); -test.serial('Throw error if github return any other errors', async t => { - const owner = 'test_user'; - const repo = 'test_repo'; - const env = {GH_TOKEN: 'github_token'}; - const github = authenticate(env) - .get(`/repos/${owner}/${repo}`) - .reply(500); +const urlFormats = [ + (owner, repo) => `https://github.com/${owner}/${repo}.git`, + (owner, repo) => `git+https://github.com/${owner}/${repo}.git`, + (owner, repo) => `http://github.com/${owner}/${repo}.git`, + (owner, repo) => `git@github.com:${owner}/${repo}.git`, + (owner, repo) => `ssh://git@github.com/${owner}/${repo}.git`, + (owner, repo) => `git://github.com/${owner}/${repo}.git`, +]; + +for (const makeRepositoryUrl of urlFormats) { + for (const make_clone_url of urlFormats) { + const owner = "test_user"; + const repo = "test_repo"; + test(`Don't throw an error if clone_url differs from repositoryUrl but owner/repo is the same -- ${makeRepositoryUrl(owner, repo)} / ${make_clone_url(owner, repo)}`, async (t) => { + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + clone_url: make_clone_url(owner, repo), + }, + { repeat: 2 }, + ); + + await t.notThrowsAsync( + verify( + {}, + { + env, + options: { + repositoryUrl: makeRepositoryUrl(owner, repo), + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.true(fetch.done()); + }); + + const repo2 = repo + "2"; + test(`Throw SemanticReleaseError if the repository is renamed -- ${makeRepositoryUrl(owner, repo)} / ${make_clone_url(owner, repo2)}`, async (t) => { + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock.sandbox().getOnce( + `https://api.github.local/repos/${owner}/${repo}`, + { + permissions: { push: true }, + clone_url: make_clone_url(owner, repo2), + }, + { repeat: 2 }, + ); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + {}, + { + env, + options: { repositoryUrl: makeRepositoryUrl(owner, repo) }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EMISMATCHGITHUBURL"); + t.true(fetch.done()); + }); + } +} + +test("Throw error if github return any other errors", async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, 500); const error = await t.throwsAsync( - verify({}, {env, options: {repositoryUrl: `https://github.com/${owner}/${repo}.git`}, logger: t.context.logger}) + verify( + {}, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); t.is(error.status, 500); - t.true(github.isDone()); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "proxy" option is not a String or an Object', async t => { - const env = {GH_TOKEN: 'github_token'}; +test('Throw SemanticReleaseError if "proxy" option is not a String or an Object', async (t) => { + const env = { GH_TOKEN: "github_token" }; const proxy = 42; - const [error] = await t.throwsAsync( + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {proxy}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { proxy }, + { + env, + options: { + repositoryUrl: "https://github.com/semantic-release/github.git", + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDPROXY'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDPROXY"); }); -test('Throw SemanticReleaseError if "proxy" option is an Object with invalid properties', async t => { - const env = {GH_TOKEN: 'github_token'}; - const proxy = {host: 42}; +test('Throw SemanticReleaseError if "proxy" option is an Object with invalid properties', async (t) => { + const env = { GH_TOKEN: "github_token" }; + const proxy = { host: 42 }; - const [error] = await t.throwsAsync( + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {proxy}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { proxy }, + { + env, + options: { + repositoryUrl: "https://github.com/semantic-release/github.git", + }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDPROXY'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDPROXY"); }); -test('Throw SemanticReleaseError if "assets" option is not a String or an Array of Objects', async t => { - const env = {GH_TOKEN: 'github_token'}; +test('Throw SemanticReleaseError if "assets" option is not a String or an Array of Objects', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; const assets = 42; - const [error] = await t.throwsAsync( + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { assets }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSETS"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "assets" option is an Array with invalid elements', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = ["file.js", 42]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assets}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSETS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSETS"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assets" option is an Array with invalid elements', async t => { - const env = {GH_TOKEN: 'github_token'}; - const assets = ['file.js', 42]; +test('Throw SemanticReleaseError if "assets" option is an Object missing the "path" property', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = { name: "file.js" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { assets }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSETS"); + t.true(fetch.done()); +}); - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "assets" option is an Array with objects missing the "path" property', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assets = [{ path: "lib/file.js" }, { name: "file.js" }]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assets}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { assets }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSETS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSETS"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assets" option is an Object missing the "path" property', async t => { - const env = {GH_TOKEN: 'github_token'}; - const assets = {name: 'file.js'}; +test('Throw SemanticReleaseError if "successComment" option is not a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const successComment = 42; - const [error] = await t.throwsAsync( + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assets}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { successComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSETS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDSUCCESSCOMMENT"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assets" option is an Array with objects missing the "path" property', async t => { - const env = {GH_TOKEN: 'github_token'}; - const assets = [{path: 'lib/file.js'}, {name: 'file.js'}]; +test('Throw SemanticReleaseError if "successComment" option is an empty String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const successComment = ""; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { successComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDSUCCESSCOMMENT"); + t.true(fetch.done()); +}); - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "successComment" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const successComment = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assets}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { successComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSETS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDSUCCESSCOMMENT"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "successComment" option is not a String', async t => { - const successComment = 42; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "failTitle" option is not a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const failTitle = 42; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {successComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failTitle }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDSUCCESSCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILTITLE"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "successComment" option is an empty String', async t => { - const successComment = ''; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "failTitle" option is an empty String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const failTitle = ""; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {successComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failTitle }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDSUCCESSCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILTITLE"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "successComment" option is a whitespace String', async t => { - const successComment = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "failTitle" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const failTitle = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {successComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failTitle }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDSUCCESSCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILTITLE"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failTitle" option is not a String', async t => { - const failTitle = 42; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "discussionCategoryName" option is not a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const discussionCategoryName = 42; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failTitle}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { discussionCategoryName }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILTITLE'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDDISCUSSIONCATEGORYNAME"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failTitle" option is an empty String', async t => { - const failTitle = ''; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "discussionCategoryName" option is an empty String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const discussionCategoryName = ""; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failTitle}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { discussionCategoryName }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILTITLE'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDDISCUSSIONCATEGORYNAME"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failTitle" option is a whitespace String', async t => { - const failTitle = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "discussionCategoryName" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const discussionCategoryName = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failTitle}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { discussionCategoryName }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILTITLE'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDDISCUSSIONCATEGORYNAME"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failComment" option is not a String', async t => { +test('Throw SemanticReleaseError if "failComment" option is not a String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; const failComment = 42; - const [error] = await t.throwsAsync( + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILCOMMENT"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failComment" option is an empty String', async t => { - const failComment = ''; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "failComment" option is an empty String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const failComment = ""; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILCOMMENT"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "failComment" option is a whitespace String', async t => { - const failComment = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "failComment" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const failComment = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {failComment}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { failComment }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDFAILCOMMENT'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDFAILCOMMENT"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "labels" option is not a String or an Array of String', async t => { - const env = {GH_TOKEN: 'github_token'}; +test('Throw SemanticReleaseError if "labels" option is not a String or an Array of String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; const labels = 42; - const [error] = await t.throwsAsync( + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {labels}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { labels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDLABELS"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "labels" option is an Array with invalid elements', async t => { - const env = {GH_TOKEN: 'github_token'}; - const labels = ['label1', 42]; - - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "labels" option is an Array with invalid elements', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const labels = ["label1", 42]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {labels}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { labels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDLABELS"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "labels" option is a whitespace String', async t => { - const labels = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "labels" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const labels = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {labels}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { labels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDLABELS"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assignees" option is not a String or an Array of String', async t => { - const env = {GH_TOKEN: 'github_token'}; +test('Throw SemanticReleaseError if "assignees" option is not a String or an Array of String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; const assignees = 42; - const [error] = await t.throwsAsync( + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assignees}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { assignees }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSIGNEES'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSIGNEES"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assignees" option is an Array with invalid elements', async t => { - const env = {GH_TOKEN: 'github_token'}; - const assignees = ['user', 42]; - - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "assignees" option is an Array with invalid elements', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assignees = ["user", 42]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assignees}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { assignees }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSIGNEES'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSIGNEES"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "assignees" option is a whitespace String', async t => { - const assignees = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "assignees" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const assignees = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {assignees}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { assignees }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDASSIGNEES'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDASSIGNEES"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "releasedLabels" option is not a String or an Array of String', async t => { - const env = {GH_TOKEN: 'github_token'}; +test('Throw SemanticReleaseError if "releasedLabels" option is not a String or an Array of String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; const releasedLabels = 42; - const [error] = await t.throwsAsync( + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { releasedLabels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDRELEASEDLABELS"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "releasedLabels" option is an Array with invalid elements', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const releasedLabels = ["label1", 42]; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { releasedLabels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDRELEASEDLABELS"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "releasedLabels" option is a whitespace String', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const releasedLabels = " \n \r "; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { releasedLabels }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDRELEASEDLABELS"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "addReleases" option is not a valid string (botom)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = "botom"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {releasedLabels}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { addReleases }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDRELEASEDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDADDRELEASES"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "releasedLabels" option is an Array with invalid elements', async t => { - const env = {GH_TOKEN: 'github_token'}; - const releasedLabels = ['label1', 42]; +test('Throw SemanticReleaseError if "addReleases" option is not a valid string (true)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = true; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { addReleases }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDADDRELEASES"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "addReleases" option is not a valid string (number)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const addReleases = 42; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { addReleases }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDADDRELEASES"); + t.true(fetch.done()); +}); + +test('Throw SemanticReleaseError if "draftRelease" option is not a valid boolean (string)', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + const draftRelease = "test"; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( + verify( + { draftRelease }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), + ); + + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDDRAFTRELEASE"); + t.true(fetch.done()); +}); - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "releaseBodyTemplate" option is an empty string', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {releasedLabels}, - {env, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { releaseBodyTemplate: "" }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDRELEASEDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDRELEASEBODYTEMPLATE"); + t.true(fetch.done()); }); -test('Throw SemanticReleaseError if "releasedLabels" option is a whitespace String', async t => { - const releasedLabels = ' \n \r '; - const [error] = await t.throwsAsync( +test('Throw SemanticReleaseError if "releaseNameTemplate" option is an empty string', async (t) => { + const owner = "test_user"; + const repo = "test_repo"; + const env = { GH_TOKEN: "github_token" }; + + const fetch = fetchMock + .sandbox() + .getOnce(`https://api.github.local/repos/${owner}/${repo}`, { + permissions: { + push: true, + }, + clone_url: `https://api.github.local/${owner}/${repo}.git`, + }); + + const { + errors: [error, ...errors], + } = await t.throwsAsync( verify( - {releasedLabels}, - {env: {}, options: {repositoryUrl: 'https://github.com/semantic-release/github.git'}, logger: t.context.logger} - ) + { releaseNameTemplate: "" }, + { + env, + options: { repositoryUrl: `https://github.com/${owner}/${repo}.git` }, + logger: t.context.logger, + }, + { + Octokit: TestOctokit.defaults((options) => ({ + ...options, + request: { ...options.request, fetch }, + })), + }, + ), ); - t.is(error.name, 'SemanticReleaseError'); - t.is(error.code, 'EINVALIDRELEASEDLABELS'); + t.is(errors.length, 0); + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDRELEASENAMETEMPLATE"); + t.true(fetch.done()); });