Compare commits

..
77 Commits
Author SHA1 Message Date
Rui Chen 8a0cd651e5 release 3.0.4 (#865)
main / build (push) Canceled after 0s
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-11-05 09:25:01 -05:00
dependabot[bot] ac01154694 chore(deps): bump the npm group across 1 directory with 4 updates (#864)
Bumps the npm group with 4 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8), [prettier](https://github.com/prettier/prettier) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.19.1 to 24.19.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 5.0.3 to 5.0.5
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.5/packages/coverage-v8)

Updates `prettier` from 3.9.9 to 3.9.10
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.9...3.9.10)

Updates `vitest` from 5.0.3 to 5.0.5
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.5/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.19.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 5.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.9.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 5.0.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-05 08:58:51 -05:00
dependabot[bot]andRui Chen ca4c9d538c chore(deps): bump minimatch and glob (#862)
* chore(deps): bump minimatch and glob

Bumps [minimatch](https://github.com/isaacs/minimatch) to 11.0.4 and updates ancestor dependency [glob](https://github.com/isaacs/node-glob). These dependencies need to be updated together.


Updates `minimatch` from 10.2.4 to 11.0.4
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v10.2.4...v11.0.4)

Updates `glob` from 13.0.6 to 14.0.3
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v13.0.6...v14.0.3)

---
updated-dependencies:
- dependency-name: glob
  dependency-version: 14.0.3
  dependency-type: direct:production
- dependency-name: minimatch
  dependency-version: 11.0.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: rebuild dist for glob 14.0.3

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-11-05 08:54:07 -05:00
dependabot[bot]andRui Chen e4a57aec13 chore(deps): bump undici from 6.29.0 to 6.29.1 (#859)
main / build (push) Canceled after 0s
* chore(deps): bump undici from 6.29.0 to 6.29.1

Bumps [undici](https://github.com/nodejs/undici) from 6.29.0 to 6.29.1.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.29.0...v6.29.1)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.29.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: rebuild dist for undici 6.29.1

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-11-04 23:12:09 -05:00
dependabot[bot] ee55a0ef7d chore(deps): bump vite from 8.3.2 to 8.3.5 (#855)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.3.2 to 8.3.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.3.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.3.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-04 23:03:26 -05:00
dependabot[bot] 8d42b221d5 chore(deps): bump actions/setup-node (#853)
Bumps the github-actions group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 7.0.0 to 7.1.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/820762786026740c76f36085b0efc47a31fe5020...949feb2413d6458794dcd2491c4babbbce0c15c1)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 7.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-04 23:03:17 -05:00
dependabot[bot] 6680cc089e chore(deps): bump the npm group with 3 updates (#851)
main / build (push) Has been cancelled
Bumps the npm group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.19.0 to 24.19.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 5.0.2 to 5.0.3
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/coverage-v8)

Updates `vitest` from 5.0.2 to 5.0.3
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.3/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.19.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 5.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 5.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-17 00:15:27 -04:00
dependabot[bot] 4a050bafc0 chore(deps): bump source-map-js from 1.2.1 to 1.2.2 (#850)
Bumps [source-map-js](https://github.com/7rulnik/source-map-js) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/7rulnik/source-map-js/releases)
- [Changelog](https://github.com/7rulnik/source-map-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/7rulnik/source-map-js/compare/v1.2.1...v1.2.2)

---
updated-dependencies:
- dependency-name: source-map-js
  dependency-version: 1.2.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-17 00:15:20 -04:00
dependabot[bot] b5a645e6d7 chore(deps): bump the npm group with 4 updates (#848)
main / build (push) Has been cancelled
Bumps the npm group with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8), [prettier](https://github.com/prettier/prettier) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.13.6 to 24.19.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 5.0.1 to 5.0.2
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.2/packages/coverage-v8)

Updates `prettier` from 3.9.8 to 3.9.9
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.8...3.9.9)

Updates `vitest` from 5.0.1 to 5.0.2
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.2/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.19.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 5.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.9.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 5.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-06 18:08:37 -04:00
dependabot[bot] d99a5de306 chore(deps): bump brace-expansion from 5.0.9 to 5.0.12 (#846)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.9 to 5.0.12.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.9...v5.0.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.12
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-06 18:08:27 -04:00
dependabot[bot]andRui Chen 4d8ab33c14 chore(deps): bump undici from 6.28.0 to 6.29.0 (#845)
main / build (push) Has been cancelled
* chore(deps): bump undici from 6.28.0 to 6.29.0

Bumps [undici](https://github.com/nodejs/undici) from 6.28.0 to 6.29.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.28.0...v6.29.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.29.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* update dist/index.js

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-09-30 08:14:45 -04:00
dependabot[bot] 5969a5da4c chore(deps): bump the npm group with 4 updates (#843)
Bumps the npm group with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8), [prettier](https://github.com/prettier/prettier) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.13.4 to 24.13.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 5.0.0 to 5.0.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.1/packages/coverage-v8)

Updates `prettier` from 3.9.6 to 3.9.8
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.8)

Updates `vitest` from 5.0.0 to 5.0.1
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.1/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.13.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 5.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.9.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 5.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-30 08:13:08 -04:00
dependabot[bot] a4f117a9dd chore(deps): bump the npm group across 1 directory with 3 updates (#842)
main / build (push) Has been cancelled
Bumps the npm group with 3 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.13.3 to 24.13.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.11 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/coverage-v8)

Updates `vitest` from 4.1.11 to 5.0.0
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v5.0.0/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.13.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 5.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-21 09:36:09 -04:00
Rui Chen efb35369e0 release 3.0.3 (#840)
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-08-29 20:27:42 -04:00
dependabot[bot] 6441963a75 chore(deps): bump the npm group with 2 updates (#839)
Bumps the npm group with 2 updates: [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@vitest/coverage-v8` from 4.1.10 to 4.1.11
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/coverage-v8)

Updates `vitest` from 4.1.10 to 4.1.11
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.11/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.11
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-29 20:15:24 -04:00
dependabot[bot] e5ee6bc58a chore(deps): bump esbuild from 0.28.1 to 0.28.2 in the npm group (#837)
main / build (push) Has been cancelled
Bumps the npm group with 1 update: [esbuild](https://github.com/evanw/esbuild).


Updates `esbuild` from 0.28.1 to 0.28.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.1...v0.28.2)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-16 16:38:50 -04:00
dependabot[bot]andRui Chen d1e66170d3 chore(deps): bump undici from 6.27.0 to 6.28.0 (#831)
main / build (push) Has been cancelled
* chore(deps): bump undici from 6.27.0 to 6.28.0

Bumps [undici](https://github.com/nodejs/undici) from 6.27.0 to 6.28.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.27.0...v6.28.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.28.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: rebuild dist bundle for undici 6.28.0

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-08-09 17:55:53 -04:00
dependabot[bot] 64037519ba chore(deps): bump the npm group with 2 updates (#835)
Bumps the npm group with 2 updates: [@octokit/plugin-retry](https://github.com/octokit/plugin-retry.js) and [@octokit/plugin-throttling](https://github.com/octokit/plugin-throttling.js).


Updates `@octokit/plugin-retry` from 8.1.0 to 8.1.1
- [Release notes](https://github.com/octokit/plugin-retry.js/releases)
- [Commits](https://github.com/octokit/plugin-retry.js/compare/v8.1.0...v8.1.1)

Updates `@octokit/plugin-throttling` from 11.0.3 to 11.0.5
- [Release notes](https://github.com/octokit/plugin-throttling.js/releases)
- [Commits](https://github.com/octokit/plugin-throttling.js/compare/v11.0.3...v11.0.5)

---
updated-dependencies:
- dependency-name: "@octokit/plugin-retry"
  dependency-version: 8.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@octokit/plugin-throttling"
  dependency-version: 11.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 17:52:26 -04:00
dependabot[bot] 7c7184b687 chore(deps): bump postcss from 8.5.19 to 8.5.25 (#833)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.19 to 8.5.25.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.19...8.5.25)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.25
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 17:52:17 -04:00
dependabot[bot] 0f3f0d2943 chore(deps): bump brace-expansion from 5.0.8 to 5.0.9 (#832)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.8 to 5.0.9.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.8...v5.0.9)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-09 17:52:08 -04:00
dependabot[bot] 77fb938f2f chore(deps): bump prettier from 3.9.5 to 3.9.6 in the npm group (#830)
main / build (push) Has been cancelled
Bumps the npm group with 1 update: [prettier](https://github.com/prettier/prettier).


Updates `prettier` from 3.9.5 to 3.9.6
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.5...3.9.6)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-03 20:10:11 -04:00
dependabot[bot] 5a6f51711c chore(deps): bump brace-expansion from 5.0.7 to 5.0.8 (#828)
main / build (push) Has been cancelled
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.7 to 5.0.8.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.7...v5.0.8)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 08:57:07 -04:00
dependabot[bot] a3c91c98f8 chore(deps): bump the github-actions group with 2 updates (#825)
Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/checkout` from 7.0.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

Updates `actions/setup-node` from 6.4.0 to 7.0.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e...820762786026740c76f36085b0efc47a31fe5020)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/setup-node
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-29 08:53:24 -04:00
dependabot[bot] 5018f9ec04 chore(deps): bump brace-expansion from 5.0.6 to 5.0.7 (#824)
main / build (push) Has been cancelled
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.6 to 5.0.7.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.6...v5.0.7)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.7
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 21:57:13 -04:00
dependabot[bot] c1e290273a chore(deps): bump the npm group with 4 updates (#823)
Bumps the npm group with 4 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8), [prettier](https://github.com/prettier/prettier) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.13.2 to 24.13.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.9 to 4.1.10
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/coverage-v8)

Updates `prettier` from 3.9.0 to 3.9.5
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.0...3.9.5)

Updates `vitest` from 4.1.9 to 4.1.10
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.13.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.9.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-22 21:57:04 -04:00
Rui Chen 1c214ab373 fix: safely classify GitHub API errors (#822)
main / build (push) Has been cancelled
* refactor: harden GitHub error classification

Signed-off-by: Rui Chen <rui@chenrui.dev>

* test: define nested GitHub status handling

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-15 11:06:56 -04:00
Rui Chen d100e75a45 docs: mark v2 as unsupported (#821)
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 13:15:35 -04:00
Rui Chen 81bcd9dd59 test: cover release lookup after tag replacement (#820)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 12:39:38 -04:00
Rui Chen b05950fac2 docs: update the release verification workflow (#819)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 10:53:12 -04:00
Rui Chen 3d0d9888cb release 3.0.2 (#818)
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 10:24:38 -04:00
Rui Chen 7e13ed4ac5 fix: clarify release creation 404 errors (#817)
* fix: clarify release creation 404 errors

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: clarify inaccessible release targets

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 10:02:44 -04:00
Rui Chen e6c70a53cf fix: replace existing release assets on Gitea (#816)
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-13 08:44:02 -04:00
fenleyandRui Chen f345337888 fix: publish existing draft releases as prereleases (#801)
* fix: respect input_draft when updating existing release

* fix: reuse draft releases before publishing

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: bound and canonicalize draft discovery

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: preserve unowned draft releases

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: recheck duplicate drafts before cleanup

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-07-13 01:59:29 -04:00
Rui Chen d8a89a2066 fix: upload small checksum assets reliably (#815)
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-12 23:26:33 -04:00
Rui Chen 45ece40c31 chore(deps): remove unused TypeScript tooling (#814)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-12 22:07:52 -04:00
Rui Chen f6b913c3f9 feat: improve release error reporting and test coverage (#813)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-12 21:52:34 -04:00
Rui Chen 15f193d7d8 chore(deps): upgrade TypeScript to 7 (#812)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-07-12 21:26:48 -04:00
dependabot[bot] cc8268d46a chore(deps): bump actions/checkout in the github-actions group (#810)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6.0.3 to 7.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 20:22:42 -04:00
dependabot[bot] fd0ed1e85b chore(deps): bump the npm group with 3 updates (#811)
Bumps the npm group with 3 updates: [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8), [prettier](https://github.com/prettier/prettier) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@vitest/coverage-v8` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/coverage-v8)

Updates `prettier` from 3.8.4 to 3.9.0
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.4...3.9.0)

Updates `vitest` from 4.1.8 to 4.1.9
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.9/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 20:22:35 -04:00
Rui Chen 132dbbba49 chore: bump node 24 pin to 24.18.0
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-06-25 12:27:27 -04:00
dependabot[bot]andRui Chen 3743f4e7d1 chore(deps): bump undici from 6.24.1 to 6.27.0 (#807)
* chore(deps): bump undici from 6.24.1 to 6.27.0

Bumps [undici](https://github.com/nodejs/undici) from 6.24.1 to 6.27.0.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v6.24.1...v6.27.0)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 6.27.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): rebuild dist for undici 6.27.0

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-06-25 12:25:02 -04:00
dependabot[bot] 566ec910ab chore(deps): bump the npm group with 2 updates (#808)
Bumps the npm group with 2 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [prettier](https://github.com/prettier/prettier).


Updates `@types/node` from 24.13.1 to 24.13.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `prettier` from 3.8.3 to 3.8.4
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.3...3.8.4)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.13.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.8.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 12:13:00 -04:00
Rui Chen 718ea10b13 release 3.0.1
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-06-19 10:41:38 -04:00
dependabot[bot]andRui Chen f1a938b9d8 chore(deps): bump esbuild from 0.28.0 to 0.28.1 (#802)
* chore(deps): bump esbuild from 0.28.0 to 0.28.1

Bumps [esbuild](https://github.com/evanw/esbuild) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.28.0...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): rebuild dist for esbuild 0.28.1

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-06-19 10:37:08 -04:00
dependabot[bot] 0066ead0de chore(deps): bump vite from 8.0.14 to 8.0.16 (#806)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.14 to 8.0.16.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.16
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 10:19:47 -04:00
dependabot[bot] dc643cac62 chore(deps): bump the npm group with 3 updates (#805)
Bumps the npm group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.12.4 to 24.13.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.7 to 4.1.8
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/coverage-v8)

Updates `vitest` from 4.1.7 to 4.1.8
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.8/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.13.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 10:19:40 -04:00
dependabot[bot] 85ee99b6b2 chore(deps): bump actions/checkout in the github-actions group (#804)
Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 10:19:30 -04:00
dependabot[bot] 9ed3cf9a68 chore(deps): bump the npm group with 2 updates (#800)
main / build (push) Has been cancelled
Bumps the npm group with 2 updates: [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@vitest/coverage-v8` from 4.1.6 to 4.1.7
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/coverage-v8)

Updates `vitest` from 4.1.6 to 4.1.7
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/vitest)

---
updated-dependencies:
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-07 16:28:00 -04:00
dependabot[bot] 3efcac8951 chore(deps): bump the npm group with 3 updates (#798)
main / build (push) Has been cancelled
Bumps the npm group with 3 updates: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@types/node` from 24.12.3 to 24.12.4
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.5 to 4.1.6
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/coverage-v8)

Updates `vitest` from 4.1.5 to 4.1.6
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.6/packages/vitest)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.12.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-29 09:38:53 -04:00
dependabot[bot] 05d6b9164a chore(deps): bump brace-expansion from 5.0.5 to 5.0.6 (#797)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.5 to 5.0.6.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.5...v5.0.6)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-29 09:38:47 -04:00
dependabot[bot] 403a5240f3 chore(deps): bump @types/node from 24.12.2 to 24.12.3 in the npm group (#796)
main / build (push) Has been cancelled
Bumps the npm group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 24.12.2 to 24.12.3
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.12.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-17 16:56:11 -04:00
dependabot[bot] 437e073e78 chore(deps): bump the npm group with 4 updates (#792)
main / build (push) Has been cancelled
Bumps the npm group with 4 updates: [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core), [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github), [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest).


Updates `@actions/core` from 3.0.0 to 3.0.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Updates `@actions/github` from 9.1.0 to 9.1.1
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

Updates `@vitest/coverage-v8` from 4.1.4 to 4.1.5
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/coverage-v8)

Updates `vitest` from 4.1.4 to 4.1.5
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-version: 3.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@actions/github"
  dependency-version: 9.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 22:09:35 -04:00
dependabot[bot] 5a1e17dcea chore(deps): bump actions/setup-node in the github-actions group (#791)
Bumps the github-actions group with 1 update: [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/setup-node` from 6.3.0 to 6.4.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](https://github.com/actions/setup-node/compare/53b83947a5a98c8d113130e565377fae1a50d02f...48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e)

---
updated-dependencies:
- dependency-name: actions/setup-node
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 22:09:28 -04:00
dependabot[bot] 2bc819c87a chore(deps): bump postcss from 8.5.9 to 8.5.10 (#789)
main / build (push) Has been cancelled
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.9 to 8.5.10.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.9...8.5.10)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.10
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-26 01:22:12 -04:00
dependabot[bot] c90b8762f6 chore(deps): bump the npm group with 2 updates (#788)
Bumps the npm group with 2 updates: [prettier](https://github.com/prettier/prettier) and [typescript](https://github.com/microsoft/TypeScript).


Updates `prettier` from 3.8.2 to 3.8.3
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.2...3.8.3)

Updates `typescript` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v6.0.2...v6.0.3)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.8.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript
  dependency-version: 6.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-25 22:38:20 -04:00
Rui Chen 4dd30955cc chore: remove .github/FUNDING.yml
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-12 11:52:07 -04:00
Rui Chen be9cff7d8e feat: add dependabot cooldown, update pinact annotations (#785)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-12 11:31:15 -04:00
Rui Chen 07399f3317 docs: clarify issue links in release summaries
main / build (push) Has been cancelled
closes #778

Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-12 08:16:23 -04:00
dependabot[bot]andRui Chen 93e4a0038b chore(deps): keep @types/node on the Node 24 line (#784)
main / build (push) Has been cancelled
* chore(deps): bump @types/node from 24.12.2 to 25.6.0 in the npm group

Bumps the npm group with 1 update: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node).


Updates `@types/node` from 24.12.2 to 25.6.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.6.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore(deps): keep @types/node on the node24 line

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-04-12 00:57:20 -04:00
Copilot b430933298 release: cut v3.0.0 for Node 24 upgrade (#670)
- feat: move action runtime to node24 and require Node >=24
- chore: bump @types/node, enable Dependabot updates, rebuild dist
- cut 3.0.0 release

Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-12 00:45:48 -04:00
dependabot[bot]andRui Chen c2e35e05a7 chore(deps): bump the npm group across 1 directory with 7 updates (#783)
* chore(deps): bump the npm group across 1 directory with 7 updates

Bumps the npm group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@actions/github](https://github.com/actions/toolkit/tree/HEAD/packages/github) | `9.0.0` | `9.1.0` |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `20.19.37` | `20.19.39` |
| [@vitest/coverage-v8](https://github.com/vitest-dev/vitest/tree/HEAD/packages/coverage-v8) | `4.1.0` | `4.1.4` |
| [esbuild](https://github.com/evanw/esbuild) | `0.27.4` | `0.28.0` |
| [prettier](https://github.com/prettier/prettier) | `3.8.1` | `3.8.2` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.9.3` | `6.0.2` |



Updates `@actions/github` from 9.0.0 to 9.1.0
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/github/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/github)

Updates `@types/node` from 20.19.37 to 20.19.39
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `@vitest/coverage-v8` from 4.1.0 to 4.1.4
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/coverage-v8)

Updates `esbuild` from 0.27.4 to 0.28.0
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.27.4...v0.28.0)

Updates `prettier` from 3.8.1 to 3.8.2
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.8.1...3.8.2)

Updates `typescript` from 5.9.3 to 6.0.2
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2)

Updates `vitest` from 4.1.0 to 4.1.4
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest)

---
updated-dependencies:
- dependency-name: "@actions/github"
  dependency-version: 9.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: "@types/node"
  dependency-version: 20.19.39
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: "@vitest/coverage-v8"
  dependency-version: 4.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: esbuild
  dependency-version: 0.28.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm
- dependency-name: prettier
  dependency-version: 3.8.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
- dependency-name: typescript
  dependency-version: 6.0.2
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: npm
- dependency-name: vitest
  dependency-version: 4.1.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: npm
...

Signed-off-by: dependabot[bot] <support@github.com>

* ci: comment on stale dist bundles

Signed-off-by: Rui Chen <rui@chenrui.dev>

* update dist/index.js

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-04-11 23:55:00 -04:00
Rui Chen 3bb12739c2 release 2.6.2
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-11 23:31:51 -04:00
Rui Chen c34030fec9 chore: bump node to 24.14.1
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-04-11 23:27:31 -04:00
dependabot[bot] 8975bd05c0 chore(deps): bump vite from 8.0.0 to 8.0.5 (#781)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.0 to 8.0.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-11 23:22:58 -04:00
dependabot[bot] f71937f44d chore(deps): bump brace-expansion from 5.0.4 to 5.0.5 (#777)
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.4 to 5.0.5.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.4...v5.0.5)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 5.0.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-11 23:22:21 -04:00
dependabot[bot] 3f0d239d58 chore(deps): bump picomatch from 4.0.3 to 4.0.4 (#775)
Bumps [picomatch](https://github.com/micromatch/picomatch) from 4.0.3 to 4.0.4.
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/4.0.3...4.0.4)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 4.0.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-11 23:22:10 -04:00
Rui Chen 153bb8e044 release 2.6.1
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 20:57:00 -04:00
Rui Chen 569deb874d fix: preserve discussion category when publishing releases (#765)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 20:54:29 -04:00
Rui Chen 26e8ad27a0 release 2.6.0
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 14:23:25 -04:00
Rui Chen b959f31e96 fix: clarify immutable prerelease uploads (#763)
* fix: draft prereleases before uploading assets

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: clarify immutable prerelease uploads

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 14:17:31 -04:00
Rui Chen 8a8510e3a0 ci: verify dist bundle freshness (#762)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 13:45:27 -04:00
Rui Chen 438c15ddf5 docs: clarify working_directory input (#761)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 13:40:27 -04:00
Rui Chen 6ca3b5d96e fix: recover concurrent asset metadata 404s (#760)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 13:32:59 -04:00
Rui Chen 11f917660b chore: add RELEASE.md
main / build (push) Has been cancelled
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 01:58:50 -04:00
Rui Chen 1f3f350167 feat: add AGENTS.md
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 01:48:19 -04:00
Rui Chen 37819cb191 docs: clarify reused draft release behavior (#759)
Signed-off-by: Rui Chen <rui@chenrui.dev>
2026-03-15 01:26:56 -04:00
Paulo CesarandRui Chen 9312864490 feat: support previous_tag for generate_release_notes (#372)
* feat: add generate from latest tag

* chore: refresh previous_tag docs and bundle

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
Co-authored-by: Rui Chen <rui@chenrui.dev>
2026-03-15 01:17:21 -04:00
24 changed files with 4826 additions and 1570 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
ko_fi: softprops
+5 -1
View File
@@ -14,7 +14,9 @@ updates:
- ">=3.0.0" - ">=3.0.0"
- dependency-name: "@types/node" - dependency-name: "@types/node"
versions: versions:
- ">=22.0.0" - ">=25.0.0"
cooldown:
default-days: 7
commit-message: commit-message:
prefix: "chore(deps)" prefix: "chore(deps)"
- package-ecosystem: github-actions - package-ecosystem: github-actions
@@ -25,5 +27,7 @@ updates:
github-actions: github-actions:
patterns: patterns:
- "*" - "*"
cooldown:
default-days: 7
commit-message: commit-message:
prefix: "chore(deps)" prefix: "chore(deps)"
+53 -9
View File
@@ -4,29 +4,73 @@ on:
push: push:
pull_request: pull_request:
permissions:
contents: read
issues: write
pull-requests: write
jobs: jobs:
build: build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v5 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6 - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with: with:
node-version-file: ".tool-versions" node-version-file: ".tool-versions"
cache: "npm" cache: "npm"
- name: Install - name: Install
run: npm ci run: npm ci
- name: Typecheck
run: npm run typecheck
- name: Build - name: Build
run: npm run build run: npm run build
- name: Check dist freshness
id: dist_freshness
continue-on-error: true
run: |
git diff --exit-code --stat -- dist/index.js \
|| (echo "##[error] found changed dist/index.js after build. please run 'npm run build' and commit the updated bundle" \
&& exit 1)
- name: Comment on stale dist bundle
if: github.event_name == 'pull_request' && steps.dist_freshness.outcome == 'failure'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
marker='<!-- dist-freshness-check -->'
diff_stat="$(git diff --stat -- dist/index.js)"
body=$(cat <<EOF
$marker
\`dist/index.js\` changed after \`npm run build\`.
Please run \`npm run build\` and commit the regenerated bundle.
\`\`\`text
$diff_stat
\`\`\`
EOF
)
existing_id="$(
gh api "repos/$GH_REPO/issues/$PR_NUMBER/comments" \
--jq '.[] | select(.user.login == "github-actions[bot]" and (.body | contains("<!-- dist-freshness-check -->"))) | .id' \
| tail -n 1
)"
if [ -n "$existing_id" ]; then
gh api --method PATCH "repos/$GH_REPO/issues/comments/$existing_id" -f body="$body" >/dev/null
else
gh api --method POST "repos/$GH_REPO/issues/$PR_NUMBER/comments" -f body="$body" >/dev/null
fi
- name: Fail on stale dist bundle
if: steps.dist_freshness.outcome == 'failure'
run: |
echo "##[error] found changed dist/index.js after build. please run 'npm run build' and commit the updated bundle"
exit 1
- name: Test - name: Test
run: npm run test run: npm run test
- name: Format - name: Format
run: npm run fmtcheck run: npm run fmtcheck
# - name: "check for uncommitted changes"
# # Ensure no changes, but ignore node_modules dir since dev/fresh ci deps installed.
# run: |
# git diff --exit-code --stat -- . ':!node_modules' \
# || (echo "##[error] found changed files after build. please 'npm run build && npm run fmt'" \
# "and check in all changes" \
# && exit 1)
+1 -1
View File
@@ -1 +1 @@
nodejs 24.11.0 nodejs 24.18.0
+80
View File
@@ -0,0 +1,80 @@
# action-gh-release
This repository is maintained as a small, user-facing GitHub Action with a relatively wide compatibility surface.
Optimize for stability, reproducibility, and clear user value over broad rewrites.
## Core Rules
- Prefer narrow behavior fixes over structural churn.
- Reproduce current behavior on `master` before changing code.
- Treat GitHub platform behavior as distinct from action behavior.
If GitHub controls the outcome, prefer docs or clearer errors over brittle workarounds.
- Do not revive stale PRs mechanically.
Reuse the idea if it still has value, but reimplement on top of current `master`.
- Avoid standalone refactors with no clear user-facing benefit.
## Current Architecture
- `src/main.ts` is the action bootstrap; `src/run.ts` orchestrates config parsing, validation, release creation/update, asset uploads, finalization, and outputs.
- `src/github.ts` owns release semantics: lookup, create/update/finalize, asset upload, race handling, and GitHub API interaction.
- `src/util.ts` owns parsing and path normalization.
- Keep behavior-specific logic in `src/github.ts` or `src/util.ts`; avoid growing `src/main.ts` with ad-hoc feature branches.
## Bug-Fix Workflow
- Reproduce the issue against current `master` first.
- When available, use the companion consumer harness repo `action-gh-release-test`.
- Capture exact workflow run URLs and release URLs before claiming a fix.
- If the issue is really a docs/usage or platform-limit case, document it and close it as such instead of forcing a code change.
- If a historical issue no longer reproduces on current `master`, prefer a short closeout note that asks the reporter to open a fresh issue if they still see it.
## Feature Triage
- Ship features only when there is clear user value or repeated demand.
- Small convenience features are fine, but they should stay small.
- Weak-demand features should not expand parsing complexity, cross-platform ambiguity, or maintenance surface.
- For old feature PRs:
- check whether current `master` already covers the behavior
- prefer a tiny docs clarification if the behavior exists but is poorly explained
- close stale feature PRs when the idea is obsolete, low-value, or badly shaped for the current codebase
## Contract Sync
When behavior changes, keep the external contract in sync:
- update `README.md`
- update `action.yml`
- update tests under `__tests__/`
- regenerate `dist/index.js` with `npm run build`
Docs-only changes do not need `dist/index.js` regeneration.
## Verification
For code changes, run:
- `npm run fmtcheck`
- `npm run typecheck`
- `npm run build`
- `npm test`
For behavior changes, also run the relevant external regression workflow(s) in `action-gh-release-test` against the exact ref under test.
## Release and Triage Conventions
- Keep PR labels accurate. Release notes depend on them.
- bug fixes: `bug`
- docs-only changes: `documentation`
- additive features: `feature` or `enhancement`
- dependency updates: `dependencies`
- Follow [RELEASE.md](RELEASE.md) for version bumps, changelog updates, tagging, and release publication.
- Prefer manual issue/PR closeouts with a short rationale over implicit assumptions.
- Do not auto-close old PRs or issues through unrelated docs PRs.
## Implementation Preferences
- Preserve the current upload/finalize flow unless there is strong evidence it needs to change.
- Prefer upload-time semantics over filesystem mutation.
- Be careful with parsing changes around `files`, path handling, and Windows compatibility.
- Be careful with race-condition fixes; verify both local tests and consumer-repo concurrency harnesses.
- Do not assume a refactor is safe just because tests are green. This action’s behavior is heavily shaped by GitHub API edge cases.
+124
View File
@@ -1,3 +1,127 @@
## 3.0.4
- maintenance release with updated dependencies
## 3.0.3
`3.0.3` is a maintenance release with updated dependencies. It also safely
classifies malformed GitHub API errors to avoid secondary failures (#822).
## What's Changed
### Bug fixes 🐛
* fix: safely classify GitHub API errors by @chenrui333 in https://github.com/softprops/action-gh-release/pull/822
### Other Changes 🔄
* dependency updates
## 3.0.2
`3.0.2` is a patch release focused on release reliability and compatibility. It
reuses existing draft releases when publishing prereleases, supports replacing
release assets on Gitea, hardens streamed asset uploads, and provides clearer
release-creation diagnostics. It also includes TypeScript, coverage, and tooling
maintenance merged since `3.0.1`.
This release fixes #795, #438, and #803. The upload transport hardening covers the
historical failure reported in #790, although current hosted Node 24 runners did
not reproduce it naturally. The diagnostics work is related to #786 and does not
claim a reproducible release-creation fix.
## What's Changed
### Exciting New Features 🎉
* feat: improve release error reporting and test coverage by @chenrui333 in https://github.com/softprops/action-gh-release/pull/813
### Bug fixes 🐛
* fix: publish existing draft releases as prereleases by @godfengliang in https://github.com/softprops/action-gh-release/pull/801
* fix: upload small checksum assets reliably by @chenrui333 in https://github.com/softprops/action-gh-release/pull/815
* fix: replace existing release assets on Gitea by @chenrui333 in https://github.com/softprops/action-gh-release/pull/816
* fix: clarify release creation 404 errors by @chenrui333 in https://github.com/softprops/action-gh-release/pull/817
### Other Changes 🔄
* chore(deps): upgrade TypeScript to 7 by @chenrui333 in https://github.com/softprops/action-gh-release/pull/812
* chore(deps): remove unused TypeScript tooling by @chenrui333 in https://github.com/softprops/action-gh-release/pull/814
* dependency, Node 24 pin, and CI maintenance merged since `3.0.1`
## 3.0.1
- maintenance release with updated dependencies
## 3.0.0
`3.0.0` is a major release that moves the action runtime from Node 20 to Node 24.
Use `v3` on GitHub-hosted runners and self-hosted fleets that already support the
Node 24 Actions runtime. `v2.6.2` was the final Node 20-compatible release and is
no longer maintained or supported.
## What's Changed
### Other Changes 🔄
* Move the action runtime and bundle target to Node 24
* Update `@types/node` to the Node 24 line and allow future Dependabot updates
* Keep the floating major tag on `v3`; freeze `v2` at the final `v2.6.2` release
## 2.6.2
`2.6.2` is the final `v2` release and is no longer maintained or supported. Upgrade
to `v3` for the supported Node 24 runtime and current fixes.
## What's Changed
### Other Changes 🔄
* chore(deps): bump picomatch from 4.0.3 to 4.0.4 by @dependabot[bot] in https://github.com/softprops/action-gh-release/pull/775
* chore(deps): bump brace-expansion from 5.0.4 to 5.0.5 by @dependabot[bot] in https://github.com/softprops/action-gh-release/pull/777
* chore(deps): bump vite from 8.0.0 to 8.0.5 by @dependabot[bot] in https://github.com/softprops/action-gh-release/pull/781
## 2.6.1
`2.6.1` is a patch release focused on restoring linked discussion thread creation when
`discussion_category_name` is set. It fixes `#764`, where the draft-first publish flow
stopped carrying the discussion category through the final publish step.
If you still hit an issue after upgrading, please open a report with the bug template and include a minimal repro or sanitized workflow snippet where possible.
## What's Changed
### Bug fixes 🐛
* fix: preserve discussion category on publish by @chenrui333 in https://github.com/softprops/action-gh-release/pull/765
## 2.6.0
`2.6.0` is a minor release centered on `previous_tag` support for `generate_release_notes`,
which lets workflows pin GitHub's comparison base explicitly instead of relying on the default range.
It also includes the recent concurrent asset upload recovery fix, a `working_directory` docs sync,
a checked-bundle freshness guard for maintainers, and clearer immutable-prerelease guidance where
GitHub platform behavior imposes constraints on how prerelease asset uploads can be published.
If you still hit an issue after upgrading, please open a report with the bug template and include a minimal repro or sanitized workflow snippet where possible.
## What's Changed
### Exciting New Features 🎉
* feat: support previous_tag for generate_release_notes by @pocesar in https://github.com/softprops/action-gh-release/pull/372
### Bug fixes 🐛
* fix: recover concurrent asset metadata 404s by @chenrui333 in https://github.com/softprops/action-gh-release/pull/760
### Other Changes 🔄
* docs: clarify reused draft release behavior by @chenrui333 in https://github.com/softprops/action-gh-release/pull/759
* docs: clarify working_directory input by @chenrui333 in https://github.com/softprops/action-gh-release/pull/761
* ci: verify dist bundle freshness by @chenrui333 in https://github.com/softprops/action-gh-release/pull/762
* fix: clarify immutable prerelease uploads by @chenrui333 in https://github.com/softprops/action-gh-release/pull/763
## 2.5.3 ## 2.5.3
`2.5.3` is a patch release focused on the remaining path-handling and release-selection bugs uncovered after `2.5.2`. `2.5.3` is a patch release focused on the remaining path-handling and release-selection bugs uncovered after `2.5.2`.
+55 -9
View File
@@ -39,6 +39,12 @@ Typically usage of this action involves adding a step to a build that
is gated pushes to git tags. You may find `step.if` field helpful in accomplishing this is gated pushes to git tags. You may find `step.if` field helpful in accomplishing this
as it maximizes the reuse value of your workflow for non-tag pushes. as it maximizes the reuse value of your workflow for non-tag pushes.
`v2.6.2` is the final `v2` release and is no longer maintained or supported. It
uses the [Node 20 runtime deprecated by GitHub Actions](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/).
Upgrade to `v3`, which runs on Node 24. If a problem still reproduces on the
latest `v3`, open a new issue with the exact action ref, workflow run URL,
runner, and relevant logs.
Below is a simple example of `step.if` tag gating Below is a simple example of `step.if` tag gating
```yaml ```yaml
@@ -53,7 +59,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
``` ```
@@ -74,7 +80,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v6
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
``` ```
### ⬆️ Uploading release assets ### ⬆️ Uploading release assets
@@ -105,7 +111,7 @@ jobs:
- name: Test - name: Test
run: cat Release.txt run: cat Release.txt
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
with: with:
files: Release.txt files: Release.txt
@@ -129,7 +135,7 @@ jobs:
- name: Test - name: Test
run: cat Release.txt run: cat Release.txt
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
with: with:
files: | files: |
@@ -141,6 +147,20 @@ jobs:
> **⚠️ Note for Windows:** Both `\` and `/` path separators are accepted in `files` globs. If you need to match a literal glob metacharacter such as `[` or `]`, keep escaping the metacharacter itself in the pattern. > **⚠️ Note for Windows:** Both `\` and `/` path separators are accepted in `files` globs. If you need to match a literal glob metacharacter such as `[` or `]`, keep escaping the metacharacter itself in the pattern.
If your release assets are generated under a subdirectory, set `working_directory`
and keep the `files` patterns relative to that directory.
```yaml
- name: Release
uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag'
with:
working_directory: dist
files: |
Release.txt
checksums/*.txt
```
### 📝 External release notes ### 📝 External release notes
Many systems exist that can help generate release notes for you. This action supports Many systems exist that can help generate release notes for you. This action supports
@@ -161,7 +181,7 @@ jobs:
- name: Generate Changelog - name: Generate Changelog
run: echo "# Good things have arrived" > ${{ github.workspace }}-CHANGELOG.txt run: echo "# Good things have arrived" > ${{ github.workspace }}-CHANGELOG.txt
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v3
if: github.ref_type == 'tag' if: github.ref_type == 'tag'
with: with:
body_path: ${{ github.workspace }}-CHANGELOG.txt body_path: ${{ github.workspace }}-CHANGELOG.txt
@@ -173,6 +193,20 @@ jobs:
token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
``` ```
When you use GitHub's built-in `generate_release_notes` support, you can optionally
pin the comparison base explicitly with `previous_tag`. This is useful when the default
comparison range does not match the release series you want to publish.
```yaml
- name: Release
uses: softprops/action-gh-release@v3
with:
tag_name: stage-2026-03-15
target_commitish: ${{ github.sha }}
previous_tag: prod-2026-03-01
generate_release_notes: true
```
### 💅 Customizing ### 💅 Customizing
#### inputs #### inputs
@@ -183,10 +217,11 @@ The following are optional as `step.with` keys
| -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `body` | String | Text communicating notable changes in this release | | `body` | String | Text communicating notable changes in this release |
| `body_path` | String | Path to load text communicating notable changes in this release | | `body_path` | String | Path to load text communicating notable changes in this release |
| `draft` | Boolean | Indicator of whether or not this release is a draft | | `draft` | Boolean | Keep the release as a draft. Defaults to false. When reusing an existing draft release, set this to true to keep it draft; omit it to publish after upload. |
| `prerelease` | Boolean | Indicator of whether or not is a prerelease | | `prerelease` | Boolean | Indicator of whether or not is a prerelease |
| `preserve_order` | Boolean | Upload assets sequentially in the provided order. This controls the action's upload behavior, but it does not control the final asset ordering that GitHub may display on the release page or return from the Releases API. | | `preserve_order` | Boolean | Upload assets sequentially in the provided order. This controls the action's upload behavior, but it does not control the final asset ordering that GitHub may display on the release page or return from the Releases API. |
| `files` | String | Newline-delimited globs of paths to assets to upload for release. Escape glob metacharacters when you need to match a literal filename that contains them, such as `[` or `]`. `~/...` expands to the runner home directory. On Windows, both `\` and `/` separators are accepted. GitHub may normalize raw asset filenames that contain special characters; the action restores the asset label when possible, but the final download name remains GitHub-controlled. | | `files` | String | Newline-delimited globs of paths to assets to upload for release. Escape glob metacharacters when you need to match a literal filename that contains them, such as `[` or `]`. `~/...` expands to the runner home directory. On Windows, both `\` and `/` separators are accepted. GitHub may normalize raw asset filenames that contain special characters; the action restores the asset label when possible, but the final download name remains GitHub-controlled. |
| `working_directory` | String | Base directory to resolve `files` globs against. Use this when release assets live under a subdirectory. If omitted, the action resolves `files` from `${{ github.workspace }}`. |
| `overwrite_files` | Boolean | Indicator of whether files should be overwritten when they already exist. Defaults to true | | `overwrite_files` | Boolean | Indicator of whether files should be overwritten when they already exist. Defaults to true |
| `name` | String | Name of the release. defaults to tag name | | `name` | String | Name of the release. defaults to tag name |
| `tag_name` | String | Name of a tag. defaults to `github.ref_name`. `refs/tags/<name>` values are normalized to `<name>`. | | `tag_name` | String | Name of a tag. defaults to `github.ref_name`. `refs/tags/<name>` values are normalized to `<name>`. |
@@ -196,15 +231,26 @@ The following are optional as `step.with` keys
| `token` | String | Authorized GitHub token or PAT. Defaults to `${{ github.token }}` when omitted. A non-empty explicit token overrides `GITHUB_TOKEN`. Passing `""` treats the token as explicitly unset, so omit the input entirely or use an expression such as `${{ inputs.token || github.token }}` when wrapping this action in a composite action. | | `token` | String | Authorized GitHub token or PAT. Defaults to `${{ github.token }}` when omitted. A non-empty explicit token overrides `GITHUB_TOKEN`. Passing `""` treats the token as explicitly unset, so omit the input entirely or use an expression such as `${{ inputs.token || github.token }}` when wrapping this action in a composite action. |
| `discussion_category_name` | String | If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository. For more information, see ["Managing categories for discussions in your repository."](https://docs.github.com/en/discussions/managing-discussions-for-your-community/managing-categories-for-discussions-in-your-repository) | | `discussion_category_name` | String | If specified, a discussion of the specified category is created and linked to the release. The value must be a category that already exists in the repository. For more information, see ["Managing categories for discussions in your repository."](https://docs.github.com/en/discussions/managing-discussions-for-your-community/managing-categories-for-discussions-in-your-repository) |
| `generate_release_notes` | Boolean | Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes. See the [GitHub docs for this feature](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes) for more information | | `generate_release_notes` | Boolean | Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes. See the [GitHub docs for this feature](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes) for more information |
| `previous_tag` | String | Optional. When `generate_release_notes` is enabled, use this tag as GitHub's `previous_tag_name` comparison base. If omitted, GitHub chooses the comparison base automatically. |
| `append_body` | Boolean | Append to existing body instead of overwriting it | | `append_body` | Boolean | Append to existing body instead of overwriting it |
| `make_latest` | String | Specifies whether this release should be set as the latest release for the repository. Drafts and prereleases cannot be set as latest. Can be `true`, `false`, or `legacy`. Uses GitHub api defaults if not provided | | `make_latest` | String | Specifies whether this release should be set as the latest release for the repository. Drafts and prereleases cannot be set as latest. Can be `true`, `false`, or `legacy`. Uses GitHub api defaults if not provided |
💡 When providing a `body` and `body_path` at the same time, `body_path` will be 💡 When providing a `body` and `body_path` at the same time, `body_path` will be
attempted first, then falling back on `body` if the path can not be read from. attempted first, then falling back on `body` if the path can not be read from.
💡 When the release info keys (such as `name`, `body`, `draft`, `prerelease`, etc.) 💡 When the release info keys (such as `name`, `body`, `prerelease`, etc.) are not
are not explicitly set and there is already an existing release for the tag, the explicitly set and there is already an existing release for the tag, the release
release will retain its original info. will retain its original info.
💡 Draft status is handled separately during finalization. If the action reuses an
existing draft release, set `draft: true` to keep it draft; if `draft` is omitted,
the action will publish that draft after uploading assets.
💡 GitHub immutable releases lock assets after publication. Standard releases in this
action already upload assets before publishing, but prereleases stay published by
default so `release.prereleased` workflows keep firing. On an immutable-release
repository, use `draft: true` for prereleases that upload assets, then publish that
draft later and subscribe downstream workflows to `release.published`.
💡 `files` is glob-based, so literal filenames that contain glob metacharacters such as 💡 `files` is glob-based, so literal filenames that contain glob metacharacters such as
`[` or `]` must be escaped in the pattern. `[` or `]` must be escaped in the pattern.
+56
View File
@@ -0,0 +1,56 @@
# Release Workflow
Use this checklist when cutting a new `action-gh-release` release.
## Inputs
- Decide the semantic version bump first: `major`, `minor`, or `patch`.
- Review recent merged PRs and labels before drafting the changelog entry.
- Make sure `master` is current and the worktree is clean before starting.
## Checklist
1. Update [package.json](package.json) to the new version.
2. Add the new entry at the top of [CHANGELOG.md](CHANGELOG.md).
- Summarize the release in 1 short paragraph.
- If the summary mentions issues, use plain `#123` references or full issue links; do not wrap issue numbers like `#123` in backticks.
- Prefer user-facing fixes and features over internal churn.
- Keep the merged PR list aligned with `.github/release.yml` categories.
3. Run `npm i` to refresh [package-lock.json](package-lock.json).
4. Run the full local verification set:
- `npm run fmtcheck`
- `npm run typecheck`
- `npm run build`
- `npm test`
5. Commit the release prep.
- Use `git commit -s` so the release commit carries a DCO sign-off.
- Use a plain release commit message like `release X.Y.Z`.
6. Push a release branch and open a pull request against `master`.
- Wait for required checks and reviews.
- Do not bypass branch protection or tag an unmerged release branch.
7. After merge, fetch `origin/master` and resolve the exact merged release commit.
- Confirm that commit contains the expected package version and top changelog entry.
- When the PR is squash-merged, do not assume the release branch commit is the release commit.
8. Create and push the full annotated version tag on the merged release commit.
- Example: `git tag -a vX.Y.Z -m "vX.Y.Z" <release-commit>`
- Push only the full version tag first, then wait for its tag-triggered CI to pass.
9. Move the floating major tag to the same merged release commit.
- For the current major line, run `npm run updatetag` to move `v3`.
- Do not move `v2`; it is frozen at the final, unsupported `v2.6.2` release.
- Verify `v3` and the full version tag are annotated and peel to the same commit.
- Verify `v2` did not move, then wait for the separate `v3` tag-triggered CI run to pass.
10. Create the GitHub release from the full version tag.
- Prefer the release body from [CHANGELOG.md](CHANGELOG.md), then let GitHub append generated notes only if they add value.
- Verify the release shows the expected tag, title, notes, draft/prerelease state, and attached artifacts.
11. Run post-release consumer verification in `ruitest2/action-gh-release-test`.
- Run the generic smoke against both the full version tag and `v3`.
- Run regression workflows relevant to the fixes in the release.
- Confirm that every disposable release, tag, discussion, container, volume, and temporary credential was cleaned up.
## Notes
- Behavior changes should already have matching updates in [README.md](README.md), [action.yml](action.yml), tests, and `dist/index.js` before release prep begins.
- Docs-only releases still need an intentional changelog entry and version bump decision.
- If a release is mainly bug fixes, keep the title and summary patch-oriented; do not bury the actual fixes under dependency noise.
- Do not move the floating major tag or publish the GitHub release until the full
version tag's CI passes.
+1773 -26
View File
File diff suppressed because it is too large. Load diff
+11
View File
@@ -0,0 +1,11 @@
import { expect, it, vi } from 'vitest';
const run = vi.hoisted(() => vi.fn().mockResolvedValue(undefined));
vi.mock('../src/run', () => ({ run }));
it('starts the action orchestration', async () => {
await import('../src/main');
expect(run).toHaveBeenCalledOnce();
});
+182
View File
@@ -0,0 +1,182 @@
import { getOctokit } from '@actions/github';
import { createServer, type Server } from 'http';
import { type AddressInfo } from 'net';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { GitHubReleaser, release, type Release } from '../src/github';
import { parseConfig, type Config } from '../src/util';
type CapturedRequest = {
path: string;
authorization: string | undefined;
contentType: string | undefined;
body: Record<string, unknown>;
};
const closeServer = async (server: Server): Promise<void> => {
server.closeIdleConnections();
server.closeAllConnections();
await new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
};
describe('release creation transport', () => {
let server: Server;
let baseUrl: string;
const requests: CapturedRequest[] = [];
const releases = new Map<string, Release>();
beforeAll(async () => {
server = createServer(async (request, response) => {
const url = new URL(request.url || '/', 'http://127.0.0.1');
const tagMatch = url.pathname.match(/^\/repos\/owner\/remote\/releases\/tags\/(.+)$/);
if (request.method === 'GET' && tagMatch) {
const release = releases.get(decodeURIComponent(tagMatch[1]));
response.writeHead(release ? 200 : 404, { 'content-type': 'application/json' });
response.end(JSON.stringify(release ?? { message: 'Not Found' }));
return;
}
if (request.method === 'GET' && url.pathname === '/repos/owner/remote/releases') {
response.writeHead(200, { 'content-type': 'application/json' });
response.end(JSON.stringify([...releases.values()]));
return;
}
if (request.method === 'POST' && url.pathname === '/repos/owner/remote/releases') {
const chunks: Buffer[] = [];
for await (const chunk of request) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
const body = JSON.parse(Buffer.concat(chunks).toString('utf8')) as Record<string, unknown>;
requests.push({
path: url.pathname,
authorization: request.headers.authorization,
contentType: request.headers['content-type'],
body,
});
const tag = String(body.tag_name);
const createdRelease: Release = {
id: requests.length,
upload_url: `http://127.0.0.1/uploads/${requests.length}`,
html_url: `http://127.0.0.1/releases/${requests.length}`,
tag_name: tag,
name: String(body.name),
body: typeof body.body === 'string' ? body.body : null,
target_commitish: 'main',
draft: Boolean(body.draft),
prerelease: Boolean(body.prerelease),
assets: [],
};
releases.set(tag, createdRelease);
response.writeHead(201, { 'content-type': 'application/json' });
response.end(JSON.stringify(createdRelease));
return;
}
response.writeHead(500, { 'content-type': 'application/json' });
response.end(
JSON.stringify({ message: `Unexpected route ${request.method} ${url.pathname}` }),
);
});
await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});
const address = server.address() as AddressInfo;
baseUrl = `http://127.0.0.1:${address.port}`;
});
afterAll(async () => {
await closeServer(server);
});
it('serializes user-facing category inputs through the real Octokit request path', async () => {
const parsedEmptyCategory = parseConfig({
INPUT_DISCUSSION_CATEGORY_NAME: '',
}).input_discussion_category_name;
expect(parsedEmptyCategory).toBeUndefined();
const cases: Array<{
name: string;
categoryProperty: 'absent' | 'present';
category: string | undefined;
expectedCategory: string | undefined;
}> = [
{
name: 'absent-category',
categoryProperty: 'absent',
category: undefined,
expectedCategory: undefined,
},
{
name: 'undefined-category',
categoryProperty: 'present',
category: undefined,
expectedCategory: undefined,
},
{
name: 'empty-input-category',
categoryProperty: 'present',
category: parsedEmptyCategory,
expectedCategory: undefined,
},
{
name: 'valid-category',
categoryProperty: 'present',
category: 'Announcements',
expectedCategory: 'Announcements',
},
];
for (const testCase of cases) {
const config: Config = {
github_token: 'not-a-real-token',
github_ref: 'refs/heads/main',
github_repository: 'owner/remote',
input_tag_name: testCase.name,
input_name: `Release ${testCase.name}`,
input_files: [],
input_draft: false,
input_prerelease: true,
input_fail_on_unmatched_files: false,
input_generate_release_notes: false,
input_append_body: false,
input_make_latest: undefined,
};
if (testCase.categoryProperty === 'present') {
config.input_discussion_category_name = testCase.category;
}
const releaser = new GitHubReleaser(
getOctokit(config.github_token, {
baseUrl,
}),
);
await expect(release(config, releaser, 1)).resolves.toMatchObject({
release: { tag_name: testCase.name },
created: true,
});
const request = requests.at(-1);
expect(request).toMatchObject({
path: '/repos/owner/remote/releases',
authorization: 'token not-a-real-token',
contentType: 'application/json; charset=utf-8',
body: {
tag_name: testCase.name,
name: `Release ${testCase.name}`,
draft: false,
prerelease: true,
generate_release_notes: false,
},
});
if (testCase.expectedCategory) {
expect(request?.body.discussion_category_name).toBe(testCase.expectedCategory);
} else {
expect(request?.body).not.toHaveProperty('discussion_category_name');
}
}
});
});
+433
View File
@@ -0,0 +1,433 @@
import type { Config } from '../src/util';
import type { Release } from '../src/github';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
setFailed: vi.fn(),
setOutput: vi.fn(),
getOctokit: vi.fn(),
GitHubReleaser: vi.fn(function () {
return mocks.releaser;
}),
releaser: { name: 'releaser' },
release: vi.fn(),
finalizeRelease: vi.fn(),
upload: vi.fn(),
listReleaseAssets: vi.fn(),
parseConfig: vi.fn(),
isTag: vi.fn(),
paths: vi.fn(),
unmatchedPatterns: vi.fn(),
uploadUrl: vi.fn(),
}));
vi.mock('@actions/core', () => ({
setFailed: mocks.setFailed,
setOutput: mocks.setOutput,
}));
vi.mock('@actions/github', () => ({ getOctokit: mocks.getOctokit }));
vi.mock('../src/github', () => ({
GitHubReleaser: mocks.GitHubReleaser,
release: mocks.release,
finalizeRelease: mocks.finalizeRelease,
upload: mocks.upload,
listReleaseAssets: mocks.listReleaseAssets,
}));
vi.mock('../src/util', async (importOriginal) => {
const actual = await importOriginal<typeof import('../src/util')>();
return {
...actual,
parseConfig: mocks.parseConfig,
isTag: mocks.isTag,
paths: mocks.paths,
unmatchedPatterns: mocks.unmatchedPatterns,
uploadUrl: mocks.uploadUrl,
};
});
import { run } from '../src/run';
const baseConfig: Config = {
github_token: 'token',
github_ref: 'refs/tags/v1.0.0',
github_repository: 'owner/repo',
input_files: [],
input_draft: false,
input_prerelease: false,
input_preserve_order: false,
input_overwrite_files: true,
input_fail_on_unmatched_files: false,
input_generate_release_notes: false,
input_append_body: false,
input_make_latest: undefined,
};
const initialRelease: Release = {
id: 41,
upload_url: 'https://uploads.example.test/releases/41/assets{?name,label}',
html_url: 'https://example.test/releases/41',
tag_name: 'v1.0.0',
name: 'v1.0.0',
target_commitish: 'main',
draft: true,
prerelease: false,
assets: [{ id: 1, name: 'existing.zip' }],
};
const finalizedRelease: Release = {
...initialRelease,
id: 42,
upload_url: 'https://uploads.example.test/releases/42/assets{?name,label}',
html_url: 'https://example.test/releases/42',
draft: false,
};
const deferred = <T>() => {
let resolve!: (value: T) => void;
const promise = new Promise<T>((resolvePromise) => {
resolve = resolvePromise;
});
return { promise, resolve };
};
describe('run', () => {
let config: Config;
beforeEach(() => {
vi.clearAllMocks();
config = { ...baseConfig };
mocks.parseConfig.mockImplementation(() => config);
mocks.isTag.mockImplementation((ref: string) => ref.startsWith('refs/tags/'));
mocks.unmatchedPatterns.mockReturnValue([]);
mocks.paths.mockReturnValue([]);
mocks.uploadUrl.mockImplementation((url: string) => url.split('{')[0]);
mocks.getOctokit.mockReturnValue({ name: 'octokit' });
mocks.release.mockResolvedValue({ release: initialRelease, created: true });
mocks.finalizeRelease.mockResolvedValue(finalizedRelease);
mocks.upload.mockResolvedValue(undefined);
mocks.listReleaseAssets.mockResolvedValue([]);
vi.spyOn(console, 'log').mockImplementation(() => undefined);
vi.spyOn(console, 'warn').mockImplementation(() => undefined);
});
afterEach(() => {
vi.restoreAllMocks();
});
it.each([
['an explicit tag', { github_ref: 'refs/heads/main', input_tag_name: 'v1.0.0' }],
['a tag ref', { github_ref: 'refs/tags/v1.0.0', input_tag_name: undefined }],
[
'a draft without a tag',
{ github_ref: 'refs/heads/main', input_tag_name: undefined, input_draft: true },
],
])('accepts %s', async (_name, patch) => {
config = { ...config, ...patch };
await run();
expect(mocks.release).toHaveBeenCalledOnce();
expect(mocks.setFailed).not.toHaveBeenCalled();
});
it('reports the documented tag requirement for a non-tag ref', async () => {
config = { ...config, github_ref: 'refs/heads/main', input_tag_name: undefined };
await run();
expect(mocks.setFailed).toHaveBeenCalledWith('⚠️ GitHub Releases requires a tag');
expect(mocks.release).not.toHaveBeenCalled();
});
it('warns with each unmatched input pattern when strict matching is disabled', async () => {
config = { ...config, input_files: ['dist/*.zip'] };
mocks.unmatchedPatterns.mockReturnValue(['dist/*.zip']);
await run();
expect(console.warn).toHaveBeenCalledWith("🤔 Pattern 'dist/*.zip' does not match any files.");
expect(mocks.setFailed).not.toHaveBeenCalled();
});
it('fails with the unmatched input pattern when strict matching is enabled', async () => {
config = {
...config,
input_files: ['dist/*.zip'],
input_fail_on_unmatched_files: true,
};
mocks.unmatchedPatterns.mockReturnValue(['dist/*.zip']);
await run();
expect(mocks.setFailed).toHaveBeenCalledWith(
"⚠️ Pattern 'dist/*.zip' does not match any files.",
);
expect(mocks.release).not.toHaveBeenCalled();
});
it.each([
[false, 'warns', '🤔 dist/*.zip does not include a valid file.'],
[true, 'fails', '⚠️ dist/*.zip does not include a valid file.'],
])(
'%s strict matching %s when resolved patterns contain no files',
async (strict, _verb, message) => {
config = {
...config,
input_files: ['dist/*.zip'],
input_fail_on_unmatched_files: strict,
};
await run();
if (strict) {
expect(mocks.setFailed).toHaveBeenCalledWith(message);
expect(mocks.finalizeRelease).not.toHaveBeenCalled();
} else {
expect(console.warn).toHaveBeenCalledWith(message);
expect(mocks.finalizeRelease).toHaveBeenCalledOnce();
}
},
);
it('forwards valid files and the working directory through upload boundaries', async () => {
config = {
...config,
input_files: ['dist/*.zip'],
input_working_directory: 'fixture',
};
mocks.paths.mockReturnValue(['fixture/dist/action.zip']);
mocks.upload.mockResolvedValue({ id: 7 });
mocks.listReleaseAssets.mockResolvedValue([{ id: 7, name: 'action.zip' }]);
await run();
expect(mocks.paths).toHaveBeenCalledWith(['dist/*.zip'], 'fixture');
expect(mocks.upload).toHaveBeenCalledWith(
config,
mocks.releaser,
'https://uploads.example.test/releases/41/assets',
'fixture/dist/action.zip',
initialRelease.assets,
initialRelease.id,
);
});
it('starts uploads concurrently by default', async () => {
config = { ...config, input_files: ['one.zip', 'two.zip'] };
mocks.paths.mockReturnValue(['one.zip', 'two.zip']);
const first = deferred<{ id: number }>();
const second = deferred<{ id: number }>();
mocks.upload.mockImplementation((_config, _releaser, _url, path) =>
path === 'one.zip' ? first.promise : second.promise,
);
const result = run();
await Promise.resolve();
expect(mocks.upload).toHaveBeenCalledTimes(2);
first.resolve({ id: 1 });
second.resolve({ id: 2 });
await result;
});
it('waits for each upload when preserve_order is enabled', async () => {
config = {
...config,
input_files: ['one.zip', 'two.zip'],
input_preserve_order: true,
};
mocks.paths.mockReturnValue(['one.zip', 'two.zip']);
const first = deferred<{ id: number }>();
const second = deferred<{ id: number }>();
mocks.upload.mockImplementation((_config, _releaser, _url, path) =>
path === 'one.zip' ? first.promise : second.promise,
);
const result = run();
await Promise.resolve();
expect(mocks.upload).toHaveBeenCalledTimes(1);
first.resolve({ id: 1 });
await vi.waitFor(() => expect(mocks.upload).toHaveBeenCalledTimes(2));
second.resolve({ id: 2 });
await result;
});
it('keeps an existing draft unpublished until its upload completes', async () => {
config = {
...config,
input_draft: false,
input_prerelease: true,
input_files: ['asset.zip'],
};
mocks.release.mockResolvedValue({ release: initialRelease, created: false });
mocks.paths.mockReturnValue(['asset.zip']);
const pendingUpload = deferred<{ id: number }>();
mocks.upload.mockReturnValue(pendingUpload.promise);
const result = run();
await Promise.resolve();
expect(mocks.upload).toHaveBeenCalledOnce();
expect(mocks.finalizeRelease).not.toHaveBeenCalled();
pendingUpload.resolve({ id: 7 });
await result;
expect(mocks.finalizeRelease).toHaveBeenCalledWith(
config,
mocks.releaser,
initialRelease,
false,
);
});
it('uses the selected moving-tag release ID for upload, finalization, and outputs', async () => {
config = {
...config,
input_tag_name: 'nightly',
input_draft: false,
input_prerelease: true,
input_files: ['nightly.zip'],
};
const selectedRelease: Release = {
...initialRelease,
id: 77,
upload_url: 'https://uploads.example.test/releases/77/assets{?name,label}',
html_url: 'https://example.test/releases/77',
tag_name: 'nightly',
name: 'nightly',
prerelease: true,
};
const publishedRelease: Release = {
...selectedRelease,
draft: false,
};
mocks.release.mockResolvedValue({ release: selectedRelease, created: false });
mocks.paths.mockReturnValue(['nightly.zip']);
mocks.upload.mockResolvedValue({ id: 9 });
mocks.finalizeRelease.mockResolvedValue(publishedRelease);
mocks.listReleaseAssets.mockResolvedValue([{ id: 9, name: 'nightly.zip' }]);
await run();
expect(mocks.upload).toHaveBeenCalledWith(
config,
mocks.releaser,
'https://uploads.example.test/releases/77/assets',
'nightly.zip',
selectedRelease.assets,
77,
);
expect(mocks.finalizeRelease).toHaveBeenCalledWith(
config,
mocks.releaser,
selectedRelease,
false,
);
expect(mocks.listReleaseAssets).toHaveBeenCalledWith(config, mocks.releaser, publishedRelease);
expect(mocks.setOutput).toHaveBeenCalledWith('id', '77');
expect(mocks.setOutput).toHaveBeenCalledWith('url', publishedRelease.html_url);
});
it('leaves an existing draft recoverable when an upload fails', async () => {
config = {
...config,
input_draft: false,
input_prerelease: true,
input_files: ['asset.zip'],
};
mocks.release.mockResolvedValue({ release: initialRelease, created: false });
mocks.paths.mockReturnValue(['asset.zip']);
mocks.upload.mockRejectedValue(new Error('upload failed'));
await run();
expect(mocks.finalizeRelease).not.toHaveBeenCalled();
expect(mocks.setFailed).toHaveBeenCalledWith('upload failed');
});
it('finalizes after uploads and outputs only newly uploaded assets without uploader data', async () => {
config = { ...config, input_files: ['one.zip', 'skipped.zip', 'two.zip'] };
mocks.paths.mockReturnValue(['one.zip', 'skipped.zip', 'two.zip']);
mocks.upload
.mockResolvedValueOnce({ id: 7 })
.mockResolvedValueOnce(null)
.mockResolvedValueOnce({ id: 9 });
mocks.listReleaseAssets.mockResolvedValue([
{ id: 1, name: 'existing.zip', uploader: { login: 'someone' } },
{ id: 7, name: 'one.zip', size: 10, uploader: { login: 'bot' } },
{ id: 9, name: 'two.zip', label: 'Two', uploader: null },
]);
await run();
expect(mocks.release.mock.invocationCallOrder[0]).toBeLessThan(
mocks.upload.mock.invocationCallOrder[0],
);
expect(mocks.upload.mock.invocationCallOrder[2]).toBeLessThan(
mocks.finalizeRelease.mock.invocationCallOrder[0],
);
expect(mocks.finalizeRelease).toHaveBeenCalledWith(
config,
mocks.releaser,
initialRelease,
true,
);
expect(mocks.listReleaseAssets).toHaveBeenCalledWith(config, mocks.releaser, finalizedRelease);
expect(mocks.setOutput).toHaveBeenCalledWith('assets', [
{ id: 7, name: 'one.zip', size: 10 },
{ id: 9, name: 'two.zip', label: 'Two' },
]);
expect(mocks.setOutput).toHaveBeenCalledWith('url', finalizedRelease.html_url);
expect(mocks.setOutput).toHaveBeenCalledWith('id', '42');
expect(mocks.setOutput).toHaveBeenCalledWith('upload_url', finalizedRelease.upload_url);
});
it('outputs an empty asset list and skips refresh when no upload returns an ID', async () => {
config = { ...config, input_files: ['skipped.zip'] };
mocks.paths.mockReturnValue(['skipped.zip']);
mocks.upload.mockResolvedValue(undefined);
await run();
expect(mocks.listReleaseAssets).not.toHaveBeenCalled();
expect(mocks.setOutput).toHaveBeenCalledWith('assets', []);
});
it('configures rate-limit and abuse callbacks without making live requests', async () => {
await run();
const options = mocks.getOctokit.mock.calls[0][1];
const request = { method: 'GET', url: '/repos/owner/repo', request: { retryCount: 0 } };
expect(options.throttle.onRateLimit(5, request)).toBe(true);
expect(
options.throttle.onRateLimit(5, { ...request, request: { retryCount: 1 } }),
).toBeUndefined();
expect(options.throttle.onAbuseLimit(10, request)).toBeUndefined();
expect(console.warn).toHaveBeenCalledWith(
'Request quota exhausted for request GET /repos/owner/repo',
);
expect(console.warn).toHaveBeenCalledWith('Abuse detected for request GET /repos/owner/repo');
});
it.each([
[new Error('release failed'), 'release failed'],
['release failed', 'release failed'],
[{ message: 'release failed' }, 'release failed'],
[null, 'Unknown error'],
[undefined, 'Unknown error'],
])('normalizes a thrown value before reporting failure', async (thrown, expected) => {
mocks.parseConfig.mockImplementation(() => {
throw thrown;
});
await run();
expect(mocks.setFailed).toHaveBeenCalledWith(expected);
});
});
+394
View File
@@ -0,0 +1,394 @@
import { getOctokit } from '@actions/github';
import { createHash } from 'crypto';
import { createServer, type Server } from 'http';
import { type AddressInfo } from 'net';
import { mkdtemp, open, rm, writeFile } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import { GitHubReleaser, upload } from '../src/github';
import type { Config } from '../src/util';
const openFile = vi.hoisted(() => vi.fn());
vi.mock('fs/promises', async (importOriginal) => {
const actual = await importOriginal<typeof import('fs/promises')>();
openFile.mockImplementation(actual.open);
return { ...actual, open: openFile };
});
type Fixture = {
name: string;
path: string;
size: number;
digest: string;
contentType: string;
bytes?: Buffer;
};
type Receipt = {
method: string | undefined;
pathname: string;
filename: string | null;
contentLength: string | undefined;
contentType: string | undefined;
size: number;
digest: string;
chunks: number;
chunkTypes: string[];
bytes?: Buffer;
};
const config: Config = {
github_token: 'not-a-real-token',
github_ref: 'refs/tags/v1.0.0',
github_repository: 'owner/repo',
input_files: [],
input_fail_on_unmatched_files: false,
input_generate_release_notes: false,
input_append_body: false,
input_make_latest: undefined,
};
const sha256 = (data: Buffer): string => createHash('sha256').update(data).digest('hex');
const expectLastFileHandleClosed = async (): Promise<void> => {
const result = openFile.mock.results.at(-1);
expect(result?.type).toBe('return');
const fileHandle = await result?.value;
await expect(fileHandle.stat()).rejects.toMatchObject({ code: 'EBADF' });
};
const closeServer = async (server: Server): Promise<void> => {
server.closeIdleConnections();
server.closeAllConnections();
await new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
};
const startUploadServer = async (
responseStatus: (requestIndex: number) => number = () => 201,
): Promise<{ server: Server; uploadUrl: string; receipts: Receipt[] }> => {
const receipts: Receipt[] = [];
const server = createServer(async (request, response) => {
const url = new URL(request.url || '/', 'http://127.0.0.1');
const hash = createHash('sha256');
const bufferedChunks: Buffer[] = [];
const chunkTypes: string[] = [];
let size = 0;
let chunks = 0;
for await (const chunk of request) {
chunkTypes.push(chunk?.constructor?.name || typeof chunk);
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
chunks += 1;
size += bytes.length;
hash.update(bytes);
if (size <= 1024 * 1024) {
bufferedChunks.push(Buffer.from(bytes));
}
}
receipts.push({
method: request.method,
pathname: url.pathname,
filename: url.searchParams.get('name'),
contentLength: request.headers['content-length'],
contentType: request.headers['content-type'],
size,
digest: hash.digest('hex'),
chunks,
chunkTypes,
bytes: size <= 1024 * 1024 ? Buffer.concat(bufferedChunks) : undefined,
});
const status = responseStatus(receipts.length - 1);
response.writeHead(status, { 'content-type': 'application/json' });
response.end(
JSON.stringify(
status === 201
? { id: 123, name: url.searchParams.get('name') }
: {
message: 'Validation Failed',
errors: [{ code: 'already_exists' }],
},
),
);
});
await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});
const address = server.address() as AddressInfo;
return {
server,
uploadUrl: `http://127.0.0.1:${address.port}/repos/owner/repo/releases/1/assets`,
receipts,
};
};
describe('release asset upload transport', () => {
let tempDirectory: string;
let fixtures: Fixture[];
beforeAll(async () => {
tempDirectory = await mkdtemp(join(tmpdir(), 'action-gh-release-upload-'));
const smallFixtures = [
{
name: 'artifact.zip.sha512',
bytes: Buffer.from(`${'a'.repeat(128)}\n`),
contentType: 'application/octet-stream',
},
{
name: 'artifact.md5',
bytes: Buffer.from(`${'b'.repeat(32)}\n`),
contentType: 'application/octet-stream',
},
{ name: 'one-byte.txt', bytes: Buffer.from([0x7f]), contentType: 'text/plain' },
{ name: 'empty.bin', bytes: Buffer.alloc(0), contentType: 'application/octet-stream' },
{
name: 'binary.bin',
bytes: Buffer.from([0x00, 0x01, 0x7f, 0x80, 0xfe, 0xff]),
contentType: 'application/octet-stream',
},
];
fixtures = [];
for (const fixture of smallFixtures) {
const path = join(tempDirectory, fixture.name);
await writeFile(path, fixture.bytes);
fixtures.push({
...fixture,
path,
size: fixture.bytes.length,
digest: sha256(fixture.bytes),
});
}
const largePath = join(tempDirectory, 'large.bin');
const largeHandle = await open(largePath, 'w');
const largeHash = createHash('sha256');
const block = Buffer.alloc(64 * 1024);
for (let index = 0; index < block.length; index += 1) {
block[index] = index % 251;
}
try {
for (let index = 0; index < 128; index += 1) {
await largeHandle.write(block);
largeHash.update(block);
}
} finally {
await largeHandle.close();
}
fixtures.push({
name: 'large.bin',
path: largePath,
size: 8 * 1024 * 1024,
digest: largeHash.digest('hex'),
contentType: 'application/octet-stream',
});
});
afterAll(async () => {
await rm(tempDirectory, { recursive: true, force: true });
});
it.each([0, 1, 2, 3, 4, 5])(
'uploads fixture %i through the real Octokit request path',
async (fixtureIndex) => {
const fixture = fixtures[fixtureIndex];
const { server, uploadUrl, receipts } = await startUploadServer();
const releaser = new GitHubReleaser(getOctokit(config.github_token));
openFile.mockClear();
try {
await expect(upload(config, releaser, uploadUrl, fixture.path, [], 1)).resolves.toEqual({
id: 123,
name: fixture.name,
});
expect(receipts).toHaveLength(1);
expect(receipts[0]).toMatchObject({
method: 'POST',
pathname: '/repos/owner/repo/releases/1/assets',
filename: fixture.name,
contentLength: String(fixture.size),
contentType: fixture.contentType,
size: fixture.size,
digest: fixture.digest,
});
if (fixture.bytes) {
expect(receipts[0].bytes).toEqual(fixture.bytes);
}
expect(new Set(receipts[0].chunkTypes)).toEqual(
fixture.size === 0 ? new Set() : new Set(['Buffer']),
);
if (fixture.size > 1024 * 1024) {
expect(receipts[0].chunks).toBeGreaterThan(1);
}
await expectLastFileHandleClosed();
} finally {
await closeServer(server);
}
},
);
it('closes the file handle when the request fails', async () => {
const fixture = fixtures[0];
const { server, uploadUrl } = await startUploadServer(() => 500);
const releaser = new GitHubReleaser(getOctokit(config.github_token));
openFile.mockClear();
try {
await expect(upload(config, releaser, uploadUrl, fixture.path, [], 1)).rejects.toThrow(
'Validation Failed',
);
await expectLastFileHandleClosed();
} finally {
await closeServer(server);
}
});
it('normalizes ArrayBuffer chunks before they reach the Octokit transport', async () => {
const fixture = fixtures[0];
const { server, uploadUrl, receipts } = await startUploadServer();
const releaser = new GitHubReleaser(getOctokit(config.github_token));
const actual = await vi.importActual<typeof import('fs/promises')>('fs/promises');
openFile.mockImplementationOnce(async (...args: Parameters<typeof actual.open>) => {
const fileHandle = await actual.open(...args);
const readableWebStream = fileHandle.readableWebStream.bind(fileHandle);
Object.defineProperty(fileHandle, 'readableWebStream', {
configurable: true,
value: () =>
readableWebStream().pipeThrough(
new TransformStream<Uint8Array, ArrayBuffer>({
transform(chunk, controller) {
controller.enqueue(chunk.slice().buffer);
},
}),
),
});
return fileHandle;
});
try {
await expect(upload(config, releaser, uploadUrl, fixture.path, [], 1)).resolves.toEqual({
id: 123,
name: fixture.name,
});
expect(receipts).toHaveLength(1);
expect(receipts[0]).toMatchObject({
size: fixture.size,
digest: fixture.digest,
});
await expectLastFileHandleClosed();
} finally {
await closeServer(server);
}
});
it('opens a fresh upload body after an already-exists response', async () => {
const fixture = fixtures[0];
const { server, uploadUrl, receipts } = await startUploadServer((index) =>
index === 0 ? 422 : 201,
);
const releaser = new GitHubReleaser(getOctokit(config.github_token));
vi.spyOn(releaser, 'listReleaseAssets').mockResolvedValue([{ id: 9, name: fixture.name }]);
const deleteReleaseAsset = vi
.spyOn(releaser, 'deleteReleaseAsset')
.mockResolvedValue(undefined);
openFile.mockClear();
try {
await expect(upload(config, releaser, uploadUrl, fixture.path, [], 1)).resolves.toEqual({
id: 123,
name: fixture.name,
});
expect(receipts).toHaveLength(2);
expect(receipts.map(({ size }) => size)).toEqual([fixture.size, fixture.size]);
expect(receipts.map(({ digest }) => digest)).toEqual([fixture.digest, fixture.digest]);
expect(openFile).toHaveBeenCalledTimes(2);
expect(deleteReleaseAsset).toHaveBeenCalledWith({
owner: 'owner',
repo: 'repo',
release_id: 1,
asset_id: 9,
});
for (const result of openFile.mock.results) {
const fileHandle = await result.value;
await expect(fileHandle.stat()).rejects.toMatchObject({ code: 'EBADF' });
}
} finally {
await closeServer(server);
}
});
it('falls back to the release-scoped delete route through the real Octokit request path', async () => {
const requests: Array<{
method: string | undefined;
pathname: string;
authorization: string | undefined;
}> = [];
const server = createServer((request, response) => {
const url = new URL(request.url || '/', 'http://127.0.0.1');
requests.push({
method: request.method,
pathname: url.pathname,
authorization: request.headers.authorization,
});
if (url.pathname === '/repos/owner/repo/releases/assets/9') {
response.writeHead(404, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'page not found' }));
return;
}
if (url.pathname === '/repos/owner/repo/releases/1/assets/9') {
response.writeHead(204);
response.end();
return;
}
response.writeHead(500, { 'content-type': 'application/json' });
response.end(JSON.stringify({ message: 'unexpected route' }));
});
await new Promise<void>((resolve, reject) => {
server.once('error', reject);
server.listen(0, '127.0.0.1', resolve);
});
const address = server.address() as AddressInfo;
const releaser = new GitHubReleaser(
getOctokit(config.github_token, {
baseUrl: `http://127.0.0.1:${address.port}`,
}),
);
try {
await expect(
releaser.deleteReleaseAsset({
owner: 'owner',
repo: 'repo',
release_id: 1,
asset_id: 9,
}),
).resolves.toBeUndefined();
expect(requests).toEqual([
{
method: 'DELETE',
pathname: '/repos/owner/repo/releases/assets/9',
authorization: 'token not-a-real-token',
},
{
method: 'DELETE',
pathname: '/repos/owner/repo/releases/1/assets/9',
authorization: 'token not-a-real-token',
},
]);
} finally {
await closeServer(server);
}
});
});
+70 -177
View File
@@ -1,5 +1,6 @@
import { import {
alignAssetName, alignAssetName,
errorMessage,
expandHomePattern, expandHomePattern,
isTag, isTag,
normalizeFilePattern, normalizeFilePattern,
@@ -25,6 +26,26 @@ describe('util', () => {
'https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets', 'https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets',
); );
}); });
it('leaves a URL without a template unchanged', () => {
assert.equal(
uploadUrl('https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets'),
'https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets',
);
});
});
describe('errorMessage', () => {
it.each([
[new Error('failed'), 'failed'],
[{ message: 'failed' }, 'failed'],
['failed', 'failed'],
[42, '42'],
[null, 'Unknown error'],
[undefined, 'Unknown error'],
])('normalizes unknown thrown values', (error, expected) => {
expect(errorMessage(error)).toBe(expected);
});
}); });
describe('parseInputFiles', () => { describe('parseInputFiles', () => {
it('parses empty strings', () => { it('parses empty strings', () => {
@@ -174,6 +195,29 @@ describe('util', () => {
}); });
}); });
describe('parseConfig', () => { describe('parseConfig', () => {
const baseParsedConfig = {
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_previous_tag: undefined,
input_make_latest: undefined,
};
it('parses basic config', () => { it('parses basic config', () => {
assert.deepStrictEqual( assert.deepStrictEqual(
parseConfig({ parseConfig({
@@ -186,55 +230,22 @@ describe('util', () => {
INPUT_TARGET_COMMITISH: '', INPUT_TARGET_COMMITISH: '',
INPUT_DISCUSSION_CATEGORY_NAME: '', INPUT_DISCUSSION_CATEGORY_NAME: '',
}), }),
{ baseParsedConfig,
github_ref: '',
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
},
); );
}); });
it('treats an empty draft input as omitted', () => {
assert.strictEqual(parseConfig({ INPUT_DRAFT: '' }).input_draft, undefined);
});
it('parses basic config with commitish', () => { it('parses basic config with commitish', () => {
assert.deepStrictEqual( assert.deepStrictEqual(
parseConfig({ parseConfig({
INPUT_TARGET_COMMITISH: 'affa18ef97bc9db20076945705aba8c516139abd', INPUT_TARGET_COMMITISH: 'affa18ef97bc9db20076945705aba8c516139abd',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_files: [],
input_overwrite_files: undefined,
input_preserve_order: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: 'affa18ef97bc9db20076945705aba8c516139abd', input_target_commitish: 'affa18ef97bc9db20076945705aba8c516139abd',
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -244,25 +255,8 @@ describe('util', () => {
INPUT_DISCUSSION_CATEGORY_NAME: 'releases', INPUT_DISCUSSION_CATEGORY_NAME: 'releases',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_files: [],
input_preserve_order: undefined,
input_name: undefined,
input_overwrite_files: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: 'releases', input_discussion_category_name: 'releases',
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -273,25 +267,20 @@ describe('util', () => {
INPUT_GENERATE_RELEASE_NOTES: 'true', INPUT_GENERATE_RELEASE_NOTES: 'true',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: true, input_generate_release_notes: true,
input_make_latest: undefined, },
);
});
it('supports an explicit previous tag for release notes generation', () => {
assert.deepStrictEqual(
parseConfig({
INPUT_PREVIOUS_TAG: ' v1.2.3 ',
}),
{
...baseParsedConfig,
input_previous_tag: 'v1.2.3',
}, },
); );
}); });
@@ -306,25 +295,11 @@ describe('util', () => {
INPUT_TOKEN: 'input-token', INPUT_TOKEN: 'input-token',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: 'input-token', github_token: 'input-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: false, input_draft: false,
input_prerelease: true, input_prerelease: true,
input_preserve_order: true, input_preserve_order: true,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -335,25 +310,8 @@ describe('util', () => {
INPUT_TOKEN: ' ', INPUT_TOKEN: ' ',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: 'env-token', github_token: 'env-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -365,25 +323,10 @@ describe('util', () => {
INPUT_TOKEN: 'input-token', INPUT_TOKEN: 'input-token',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: 'input-token', github_token: 'input-token',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: false, input_draft: false,
input_prerelease: true, input_prerelease: true,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -394,25 +337,9 @@ describe('util', () => {
INPUT_PRERELEASE: 'true', INPUT_PRERELEASE: 'true',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: false, input_draft: false,
input_prerelease: true, input_prerelease: true,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
@@ -422,24 +349,7 @@ describe('util', () => {
INPUT_MAKE_LATEST: 'false', INPUT_MAKE_LATEST: 'false',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: false,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_name: undefined,
input_overwrite_files: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: 'false', input_make_latest: 'false',
}, },
); );
@@ -450,25 +360,8 @@ describe('util', () => {
INPUT_APPEND_BODY: 'true', INPUT_APPEND_BODY: 'true',
}), }),
{ {
github_ref: '', ...baseParsedConfig,
github_repository: '',
github_token: '',
input_working_directory: undefined,
input_append_body: true, input_append_body: true,
input_body: undefined,
input_body_path: undefined,
input_draft: undefined,
input_prerelease: undefined,
input_preserve_order: undefined,
input_files: [],
input_overwrite_files: undefined,
input_name: undefined,
input_tag_name: undefined,
input_fail_on_unmatched_files: false,
input_target_commitish: undefined,
input_discussion_category_name: undefined,
input_generate_release_notes: false,
input_make_latest: undefined,
}, },
); );
}); });
+7 -3
View File
@@ -16,7 +16,7 @@ inputs:
description: "Gives a tag name. Defaults to github.ref_name. refs/tags/<name> values are normalized to <name>." description: "Gives a tag name. Defaults to github.ref_name. refs/tags/<name> values are normalized to <name>."
required: false required: false
draft: draft:
description: "Creates a draft release. Defaults to false" description: "Keeps the release as a draft. Defaults to false. When reusing an existing draft release, set this to true to keep it draft; omit it to publish after upload. On immutable-release repositories, use this for prereleases that upload assets and publish the draft later."
required: false required: false
prerelease: prerelease:
description: "Identify the release as a prerelease. Defaults to false" description: "Identify the release as a prerelease. Defaults to false"
@@ -28,7 +28,7 @@ inputs:
description: "Newline-delimited list of path globs for asset files to upload. Escape glob metacharacters when matching literal filenames that contain them. `~/...` expands to the runner home directory. On Windows, both \\ and / path separators are accepted. GitHub may normalize raw asset filenames that contain special characters; the action restores the asset label when possible, but the final download name remains GitHub-controlled." description: "Newline-delimited list of path globs for asset files to upload. Escape glob metacharacters when matching literal filenames that contain them. `~/...` expands to the runner home directory. On Windows, both \\ and / path separators are accepted. GitHub may normalize raw asset filenames that contain special characters; the action restores the asset label when possible, but the final download name remains GitHub-controlled."
required: false required: false
working_directory: working_directory:
description: "Base directory to resolve 'files' globs against (defaults to job working-directory)" description: "Base directory to resolve 'files' globs against. Defaults to the workspace root used by the action step."
required: false required: false
overwrite_files: overwrite_files:
description: "Overwrite existing files with the same name. Defaults to true" description: "Overwrite existing files with the same name. Defaults to true"
@@ -53,6 +53,10 @@ inputs:
generate_release_notes: generate_release_notes:
description: "Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes." description: "Whether to automatically generate the name and body for this release. If name is specified, the specified name will be used; otherwise, a name will be automatically generated. If body is specified, the body will be pre-pended to the automatically generated notes."
required: false required: false
previous_tag:
description: "Optional. When generate_release_notes is enabled, use this tag as GitHub's previous_tag_name comparison base. If omitted, GitHub chooses the comparison base automatically."
required: false
default: ""
append_body: append_body:
description: "Append to existing body instead of overwriting it. Default is false." description: "Append to existing body instead of overwriting it. Default is false."
required: false required: false
@@ -71,7 +75,7 @@ outputs:
assets: assets:
description: "JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)" description: "JSON array containing information about each uploaded asset, in the format given [here](https://docs.github.com/en/rest/reference/repos#upload-a-release-asset--code-samples) (minus the `uploader` field)"
runs: runs:
using: "node20" using: "node24"
main: "dist/index.js" main: "dist/index.js"
branding: branding:
color: "green" color: "green"
+37 -42
View File
@@ -1,62 +1,57 @@
"use strict";var Sw=Object.create;var hp=Object.defineProperty;var Uw=Object.getOwnPropertyDescriptor;var Nw=Object.getOwnPropertyNames;var Gw=Object.getPrototypeOf,Mw=Object.prototype.hasOwnProperty;var Q=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Lw=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Nw(t))!Mw.call(e,i)&&i!==s&&hp(e,i,{get:()=>t[i],enumerable:!(r=Uw(t,i))||r.enumerable});return e};var de=(e,t,s)=>(s=e!=null?Sw(Gw(e)):{},Lw(t||!e||!e.__esModule?hp(s,"default",{value:e,enumerable:!0}):s,e));var bp=Q(Ks=>{"use strict";var K_=require("net"),Jw=require("tls"),eA=require("http"),Cp=require("https"),Pw=require("events"),X_=require("assert"),Hw=require("util");Ks.httpOverHttp=Vw;Ks.httpsOverHttp=qw;Ks.httpOverHttps=Ww;Ks.httpsOverHttps=jw;function Vw(e){var t=new Rt(e);return t.request=eA.request,t}function qw(e){var t=new Rt(e);return t.request=eA.request,t.createSocket=Ip,t.defaultPort=443,t}function Ww(e){var t=new Rt(e);return t.request=Cp.request,t}function jw(e){var t=new Rt(e);return t.request=Cp.request,t.createSocket=Ip,t.defaultPort=443,t}function Rt(e){var t=this;t.options=e||{},t.proxyOptions=t.options.proxy||{},t.maxSockets=t.options.maxSockets||eA.Agent.defaultMaxSockets,t.requests=[],t.sockets=[],t.on("free",function(r,i,o,n){for(var a=wp(i,o,n),A=0,c=t.requests.length;A<c;++A){var u=t.requests[A];if(u.host===a.host&&u.port===a.port){t.requests.splice(A,1),u.request.onSocket(r);return}}r.destroy(),t.removeSocket(r)})}Hw.inherits(Rt,Pw.EventEmitter);Rt.prototype.addRequest=function(t,s,r,i){var o=this,n=tA({request:t},o.options,wp(s,r,i));if(o.sockets.length>=this.maxSockets){o.requests.push(n);return}o.createSocket(n,function(a){a.on("free",A),a.on("close",c),a.on("agentRemove",c),t.onSocket(a);function A(){o.emit("free",a,n)}function c(u){o.removeSocket(a),a.removeListener("free",A),a.removeListener("close",c),a.removeListener("agentRemove",c)}})};Rt.prototype.createSocket=function(t,s){var r=this,i={};r.sockets.push(i);var o=tA({},r.proxyOptions,{method:"CONNECT",path:t.host+":"+t.port,agent:!1,headers:{host:t.host+":"+t.port}});t.localAddress&&(o.localAddress=t.localAddress),o.proxyAuth&&(o.headers=o.headers||{},o.headers["Proxy-Authorization"]="Basic "+new Buffer(o.proxyAuth).toString("base64")),Zt("making CONNECT request");var n=r.request(o);n.useChunkedEncodingByDefault=!1,n.once("response",a),n.once("upgrade",A),n.once("connect",c),n.once("error",u),n.end();function a(l){l.upgrade=!0}function A(l,p,g){process.nextTick(function(){c(l,p,g)})}function c(l,p,g){if(n.removeAllListeners(),p.removeAllListeners(),l.statusCode!==200){Zt("tunneling socket could not be established, statusCode=%d",l.statusCode),p.destroy();var h=new Error("tunneling socket could not be established, statusCode="+l.statusCode);h.code="ECONNRESET",t.request.emit("error",h),r.removeSocket(i);return}if(g.length>0){Zt("got illegal response body from proxy"),p.destroy();var h=new Error("got illegal response body from proxy");h.code="ECONNRESET",t.request.emit("error",h),r.removeSocket(i);return}return Zt("tunneling connection has established"),r.sockets[r.sockets.indexOf(i)]=p,s(p)}function u(l){n.removeAllListeners(),Zt(`tunneling socket could not be established, cause=%s "use strict";var Yb=Object.create;var sg=Object.defineProperty;var Ob=Object.getOwnPropertyDescriptor;var Pb=Object.getOwnPropertyNames;var Jb=Object.getPrototypeOf,Hb=Object.prototype.hasOwnProperty;var Q=(e,t)=>()=>{try{return t||e((t={exports:{}}).exports,t),t.exports}catch(s){throw t=0,s}};var Vb=(e,t,s,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Pb(t))!Hb.call(e,r)&&r!==s&&sg(e,r,{get:()=>t[r],enumerable:!(i=Ob(t,r))||i.enumerable});return e};var Qe=(e,t,s)=>(s=e!=null?Yb(Jb(e)):{},Vb(t||!e||!e.__esModule?sg(s,"default",{value:e,enumerable:!0}):s,e));var pg=Q(oi=>{"use strict";var uY=require("net"),zb=require("tls"),IA=require("http"),cg=require("https"),Zb=require("events"),pY=require("assert"),Kb=require("util");oi.httpOverHttp=Xb;oi.httpsOverHttp=$b;oi.httpOverHttps=ey;oi.httpsOverHttps=ty;function Xb(e){var t=new Ft(e);return t.request=IA.request,t}function $b(e){var t=new Ft(e);return t.request=IA.request,t.createSocket=lg,t.defaultPort=443,t}function ey(e){var t=new Ft(e);return t.request=cg.request,t}function ty(e){var t=new Ft(e);return t.request=cg.request,t.createSocket=lg,t.defaultPort=443,t}function Ft(e){var t=this;t.options=e||{},t.proxyOptions=t.options.proxy||{},t.maxSockets=t.options.maxSockets||IA.Agent.defaultMaxSockets,t.requests=[],t.sockets=[],t.on("free",function(i,r,n,o){for(var a=ug(r,n,o),A=0,c=t.requests.length;A<c;++A){var l=t.requests[A];if(l.host===a.host&&l.port===a.port){t.requests.splice(A,1),l.request.onSocket(i);return}}i.destroy(),t.removeSocket(i)})}Kb.inherits(Ft,Zb.EventEmitter);Ft.prototype.addRequest=function(t,s,i,r){var n=this,o=wA({request:t},n.options,ug(s,i,r));if(n.sockets.length>=this.maxSockets){n.requests.push(o);return}n.createSocket(o,function(a){a.on("free",A),a.on("close",c),a.on("agentRemove",c),t.onSocket(a);function A(){n.emit("free",a,o)}function c(l){n.removeSocket(a),a.removeListener("free",A),a.removeListener("close",c),a.removeListener("agentRemove",c)}})};Ft.prototype.createSocket=function(t,s){var i=this,r={};i.sockets.push(r);var n=wA({},i.proxyOptions,{method:"CONNECT",path:t.host+":"+t.port,agent:!1,headers:{host:t.host+":"+t.port}});t.localAddress&&(n.localAddress=t.localAddress),n.proxyAuth&&(n.headers=n.headers||{},n.headers["Proxy-Authorization"]="Basic "+new Buffer(n.proxyAuth).toString("base64")),Zt("making CONNECT request");var o=i.request(n);o.useChunkedEncodingByDefault=!1,o.once("response",a),o.once("upgrade",A),o.once("connect",c),o.once("error",l),o.end();function a(u){u.upgrade=!0}function A(u,p,h){process.nextTick(function(){c(u,p,h)})}function c(u,p,h){if(o.removeAllListeners(),p.removeAllListeners(),u.statusCode!==200){Zt("tunneling socket could not be established, statusCode=%d",u.statusCode),p.destroy();var d=new Error("tunneling socket could not be established, statusCode="+u.statusCode);d.code="ECONNRESET",t.request.emit("error",d),i.removeSocket(r);return}if(h.length>0){Zt("got illegal response body from proxy"),p.destroy();var d=new Error("got illegal response body from proxy");d.code="ECONNRESET",t.request.emit("error",d),i.removeSocket(r);return}return Zt("tunneling connection has established"),i.sockets[i.sockets.indexOf(r)]=p,s(p)}function l(u){o.removeAllListeners(),Zt(`tunneling socket could not be established, cause=%s
`,l.message,l.stack);var p=new Error("tunneling socket could not be established, cause="+l.message);p.code="ECONNRESET",t.request.emit("error",p),r.removeSocket(i)}};Rt.prototype.removeSocket=function(t){var s=this.sockets.indexOf(t);if(s!==-1){this.sockets.splice(s,1);var r=this.requests.shift();r&&this.createSocket(r,function(i){r.request.onSocket(i)})}};function Ip(e,t){var s=this;Rt.prototype.createSocket.call(s,e,function(r){var i=e.request.getHeader("host"),o=tA({},s.options,{socket:r,servername:i?i.replace(/:.*$/,""):e.host}),n=Jw.connect(0,o);s.sockets[s.sockets.indexOf(r)]=n,t(n)})}function wp(e,t,s){return typeof e=="string"?{host:e,port:t,localAddress:s}:e}function tA(e){for(var t=1,s=arguments.length;t<s;++t){var r=arguments[t];if(typeof r=="object")for(var i=Object.keys(r),o=0,n=i.length;o<n;++o){var a=i[o];r[a]!==void 0&&(e[a]=r[a])}}return e}var Zt;process.env.NODE_DEBUG&&/\btunnel\b/.test(process.env.NODE_DEBUG)?Zt=function(){var e=Array.prototype.slice.call(arguments);typeof e[0]=="string"?e[0]="TUNNEL: "+e[0]:e.unshift("TUNNEL:"),console.error.apply(console,e)}:Zt=function(){};Ks.debug=Zt});var sA=Q((e2,yp)=>{yp.exports=bp()});var j=Q((t2,xp)=>{xp.exports={kClose:Symbol("close"),kDestroy:Symbol("destroy"),kDispatch:Symbol("dispatch"),kUrl:Symbol("url"),kWriting:Symbol("writing"),kResuming:Symbol("resuming"),kQueue:Symbol("queue"),kConnect:Symbol("connect"),kConnecting:Symbol("connecting"),kKeepAliveDefaultTimeout:Symbol("default keep alive timeout"),kKeepAliveMaxTimeout:Symbol("max keep alive timeout"),kKeepAliveTimeoutThreshold:Symbol("keep alive timeout threshold"),kKeepAliveTimeoutValue:Symbol("keep alive timeout"),kKeepAlive:Symbol("keep alive"),kHeadersTimeout:Symbol("headers timeout"),kBodyTimeout:Symbol("body timeout"),kServerName:Symbol("server name"),kLocalAddress:Symbol("local address"),kHost:Symbol("host"),kNoRef:Symbol("no ref"),kBodyUsed:Symbol("used"),kBody:Symbol("abstracted request body"),kRunning:Symbol("running"),kBlocking:Symbol("blocking"),kPending:Symbol("pending"),kSize:Symbol("size"),kBusy:Symbol("busy"),kQueued:Symbol("queued"),kFree:Symbol("free"),kConnected:Symbol("connected"),kClosed:Symbol("closed"),kNeedDrain:Symbol("need drain"),kReset:Symbol("reset"),kDestroyed:Symbol.for("nodejs.stream.destroyed"),kResume:Symbol("resume"),kOnError:Symbol("on error"),kMaxHeadersSize:Symbol("max headers size"),kRunningIdx:Symbol("running index"),kPendingIdx:Symbol("pending index"),kError:Symbol("error"),kClients:Symbol("clients"),kClient:Symbol("client"),kParser:Symbol("parser"),kOnDestroyed:Symbol("destroy callbacks"),kPipelining:Symbol("pipelining"),kSocket:Symbol("socket"),kHostHeader:Symbol("host header"),kConnector:Symbol("connector"),kStrictContentLength:Symbol("strict content length"),kMaxRedirections:Symbol("maxRedirections"),kMaxRequests:Symbol("maxRequestsPerClient"),kProxy:Symbol("proxy agent options"),kCounter:Symbol("socket request counter"),kInterceptors:Symbol("dispatch interceptors"),kMaxResponseSize:Symbol("max response size"),kHTTP2Session:Symbol("http2Session"),kHTTP2SessionState:Symbol("http2Session state"),kRetryHandlerDefaultRetry:Symbol("retry agent default retry"),kConstruct:Symbol("constructable"),kListeners:Symbol("listeners"),kHTTPContext:Symbol("http context"),kMaxConcurrentStreams:Symbol("max concurrent streams"),kNoProxyAgent:Symbol("no proxy agent"),kHttpProxyAgent:Symbol("http proxy agent"),kHttpsProxyAgent:Symbol("https proxy agent")}});var L=Q((s2,Kp)=>{"use strict";var vp=Symbol.for("undici.error.UND_ERR"),z=class extends Error{constructor(t){super(t),this.name="UndiciError",this.code="UND_ERR"}static[Symbol.hasInstance](t){return t&&t[vp]===!0}[vp]=!0},kp=Symbol.for("undici.error.UND_ERR_CONNECT_TIMEOUT"),rA=class extends z{constructor(t){super(t),this.name="ConnectTimeoutError",this.message=t||"Connect Timeout Error",this.code="UND_ERR_CONNECT_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[kp]===!0}[kp]=!0},Dp=Symbol.for("undici.error.UND_ERR_HEADERS_TIMEOUT"),iA=class extends z{constructor(t){super(t),this.name="HeadersTimeoutError",this.message=t||"Headers Timeout Error",this.code="UND_ERR_HEADERS_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[Dp]===!0}[Dp]=!0},Rp=Symbol.for("undici.error.UND_ERR_HEADERS_OVERFLOW"),oA=class extends z{constructor(t){super(t),this.name="HeadersOverflowError",this.message=t||"Headers Overflow Error",this.code="UND_ERR_HEADERS_OVERFLOW"}static[Symbol.hasInstance](t){return t&&t[Rp]===!0}[Rp]=!0},Tp=Symbol.for("undici.error.UND_ERR_BODY_TIMEOUT"),nA=class extends z{constructor(t){super(t),this.name="BodyTimeoutError",this.message=t||"Body Timeout Error",this.code="UND_ERR_BODY_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[Tp]===!0}[Tp]=!0},Fp=Symbol.for("undici.error.UND_ERR_RESPONSE_STATUS_CODE"),aA=class extends z{constructor(t,s,r,i){super(t),this.name="ResponseStatusCodeError",this.message=t||"Response Status Code Error",this.code="UND_ERR_RESPONSE_STATUS_CODE",this.body=i,this.status=s,this. Line truncated `,u.message,u.stack);var p=new Error("tunneling socket could not be established, cause="+u.message);p.code="ECONNRESET",t.request.emit("error",p),i.removeSocket(r)}};Ft.prototype.removeSocket=function(t){var s=this.sockets.indexOf(t);if(s!==-1){this.sockets.splice(s,1);var i=this.requests.shift();i&&this.createSocket(i,function(r){i.request.onSocket(r)})}};function lg(e,t){var s=this;Ft.prototype.createSocket.call(s,e,function(i){var r=e.request.getHeader("host"),n=wA({},s.options,{socket:i,servername:r?r.replace(/:.*$/,""):e.host}),o=zb.connect(0,n);s.sockets[s.sockets.indexOf(i)]=o,t(o)})}function ug(e,t,s){return typeof e=="string"?{host:e,port:t,localAddress:s}:e}function wA(e){for(var t=1,s=arguments.length;t<s;++t){var i=arguments[t];if(typeof i=="object")for(var r=Object.keys(i),n=0,o=r.length;n<o;++n){var a=r[n];i[a]!==void 0&&(e[a]=i[a])}}return e}var Zt;process.env.NODE_DEBUG&&/\btunnel\b/.test(process.env.NODE_DEBUG)?Zt=function(){var e=Array.prototype.slice.call(arguments);typeof e[0]=="string"?e[0]="TUNNEL: "+e[0]:e.unshift("TUNNEL:"),console.error.apply(console,e)}:Zt=function(){};oi.debug=Zt});var bA=Q((hY,gg)=>{gg.exports=pg()});var Z=Q((dY,hg)=>{hg.exports={kClose:Symbol("close"),kDestroy:Symbol("destroy"),kDispatch:Symbol("dispatch"),kUrl:Symbol("url"),kWriting:Symbol("writing"),kResuming:Symbol("resuming"),kQueue:Symbol("queue"),kConnect:Symbol("connect"),kConnecting:Symbol("connecting"),kKeepAliveDefaultTimeout:Symbol("default keep alive timeout"),kKeepAliveMaxTimeout:Symbol("max keep alive timeout"),kKeepAliveTimeoutThreshold:Symbol("keep alive timeout threshold"),kKeepAliveTimeoutValue:Symbol("keep alive timeout"),kKeepAlive:Symbol("keep alive"),kHeadersTimeout:Symbol("headers timeout"),kBodyTimeout:Symbol("body timeout"),kServerName:Symbol("server name"),kLocalAddress:Symbol("local address"),kHost:Symbol("host"),kNoRef:Symbol("no ref"),kBodyUsed:Symbol("used"),kBody:Symbol("abstracted request body"),kRunning:Symbol("running"),kBlocking:Symbol("blocking"),kPending:Symbol("pending"),kSize:Symbol("size"),kBusy:Symbol("busy"),kQueued:Symbol("queued"),kFree:Symbol("free"),kConnected:Symbol("connected"),kClosed:Symbol("closed"),kNeedDrain:Symbol("need drain"),kReset:Symbol("reset"),kDestroyed:Symbol.for("nodejs.stream.destroyed"),kResume:Symbol("resume"),kOnError:Symbol("on error"),kMaxHeadersSize:Symbol("max headers size"),kRunningIdx:Symbol("running index"),kPendingIdx:Symbol("pending index"),kError:Symbol("error"),kClients:Symbol("clients"),kClient:Symbol("client"),kParser:Symbol("parser"),kOnDestroyed:Symbol("destroy callbacks"),kPipelining:Symbol("pipelining"),kSocket:Symbol("socket"),kHostHeader:Symbol("host header"),kConnector:Symbol("connector"),kStrictContentLength:Symbol("strict content length"),kMaxRedirections:Symbol("maxRedirections"),kMaxRequests:Symbol("maxRequestsPerClient"),kProxy:Symbol("proxy agent options"),kCounter:Symbol("socket request counter"),kInterceptors:Symbol("dispatch interceptors"),kMaxResponseSize:Symbol("max response size"),kHTTP2Session:Symbol("http2Session"),kHTTP2SessionState:Symbol("http2Session state"),kRetryHandlerDefaultRetry:Symbol("retry agent default retry"),kConstruct:Symbol("constructable"),kListeners:Symbol("listeners"),kHTTPContext:Symbol("http context"),kMaxConcurrentStreams:Symbol("max concurrent streams"),kNoProxyAgent:Symbol("no proxy agent"),kHttpProxyAgent:Symbol("http proxy agent"),kHttpsProxyAgent:Symbol("https proxy agent")}});var Y=Q((EY,_g)=>{"use strict";var dg=Symbol.for("undici.error.UND_ERR"),K=class extends Error{constructor(t){super(t),this.name="UndiciError",this.code="UND_ERR"}static[Symbol.hasInstance](t){return t&&t[dg]===!0}[dg]=!0},Eg=Symbol.for("undici.error.UND_ERR_CONNECT_TIMEOUT"),yA=class extends K{constructor(t){super(t),this.name="ConnectTimeoutError",this.message=t||"Connect Timeout Error",this.code="UND_ERR_CONNECT_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[Eg]===!0}[Eg]=!0},mg=Symbol.for("undici.error.UND_ERR_HEADERS_TIMEOUT"),xA=class extends K{constructor(t){super(t),this.name="HeadersTimeoutError",this.message=t||"Headers Timeout Error",this.code="UND_ERR_HEADERS_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[mg]===!0}[mg]=!0},fg=Symbol.for("undici.error.UND_ERR_HEADERS_OVERFLOW"),vA=class extends K{constructor(t){super(t),this.name="HeadersOverflowError",this.message=t||"Headers Overflow Error",this.code="UND_ERR_HEADERS_OVERFLOW"}static[Symbol.hasInstance](t){return t&&t[fg]===!0}[fg]=!0},Qg=Symbol.for("undici.error.UND_ERR_BODY_TIMEOUT"),kA=class extends K{constructor(t){super(t),this.name="BodyTimeoutError",this.message=t||"Body Timeout Error",this.code="UND_ERR_BODY_TIMEOUT"}static[Symbol.hasInstance](t){return t&&t[Qg]===!0}[Qg]=!0},Bg=Symbol.for("undici.error.UND_ERR_RESPONSE_STATUS_CODE"),DA=class extends K{constructor(t,s,i,r){super(t),this.name="ResponseStatusCodeError",this.message=t||"Response Status Code Error",this.code="UND_ERR_RESPONSE_STATUS_CODE",this.body=r,this.status=s,this. Line truncated
`)||e.includes("\r")||e.includes("\0"))===!1}function lx(e,t){let{headersList:s}=t,r=(s.get("referrer-policy",!0)??"").split(","),i="";if(r.length>0)for(let o=r.length;o!==0;o--){let n=r[o-1].trim();if(jy.has(n)){i=n;break}}i!==""&&(e.referrerPolicy=i)}function ux(){return"allowed"}function px(){return"success"}function gx(){return"success"}function hx(e){let t=null;t=e.mode,e.headersList.set("sec-fetch-mode",t,!0)}function dx(e){let t=e.origin;if(!(t==="client"||t===void 0)){if(e.responseTainting==="cors"||e.mode==="websocket")e.headersList.append("origin",t,!0);else if(e.method!=="GET"&&e.method!=="HEAD"){switch(e.referrerPolicy){case"no-referrer":t=null;break;case"no-referrer-when-downgrade":case"strict-origin":case"strict-origin-when-cross-origin":e.origin&&XA(e.origin)&&!XA(Ai(e))&&(t=null);break;case"same-origin":Jo(e,Ai(e))||(t=null);break;default:}e.headersList.append("origin",t,!0)}}}function ar(e,t){return e}function Ex(e,t,s){return!e?.startTime||e.startTime<t?{domainLookupStartTime:t,domainLookupEndTime:t,connectionStartTime:t,connectionEndTime:t,secureConnectionStartTime:t,ALPNNegotiatedProtocol:e?.ALPNNegotiatedProtocol}:{domainLookupStartTime:ar(e.domainLookupStartTime,s),domainLookupEndTime:ar(e.domainLookupEndTime,s),connectionStartTime:ar(e.connectionStartTime,s),connectionEndTime:ar(e.connectionEndTime,s),secureConnectionStartTime:ar(e.secureConnectionStartTime,s),ALPNNegotiatedProtocol:e.ALPNNegotiatedProtocol}}function mx(e){return ar($y.now(),e)}function fx(e){return{startTime:e.startTime??0,redirectStartTime:0,redirectEndTime:0,postRedirectStartTime:e.startTime??0,finalServiceWorkerStartTime:0,finalNetworkResponseStartTime:0,finalNetworkRequestStartTime:0,endTime:0,encodedBodySize:0,decodedBodySize:0,finalConnectionTimingInfo:null}}function uh(){return{referrerPolicy:"strict-origin-when-cross-origin"}}function Qx(e){return{referrerPolicy:e.referrerPolicy}}function Bx(e){let t=e.referrerPolicy;ws(t);let s=null;if(e.referrer==="client"){let a=oh();if(!a||a.origin==="null")return"no-referrer";s=new URL(a)}else e.referrer instanceof URL&&(s=e.referrer);let r=KA(s),i=KA(s,!0);r.toString().length>4096&&(r=i);let o=Jo(e,r),n=ai(r)&&!ai(e.url);switch(t){case"origin":return i??KA(s,!0);case"unsafe-url":return r;case"same-origin":return o?i:"no-referrer";case"origin-when-cross-origin":return o?r:i;case"strict-origin-when-cross-origin":{let a=Ai(e);return Jo(r,a)?r:ai(r)&&!ai(a)?"no-referrer":i}default:return n?"no-referrer":i}}function KA(e,t){return ws(e instanceof URL),e=new URL(e),e.protocol==="file:"||e.protocol==="about:"||e.protocol==="blank:"?"no-referrer":(e.username="",e.password="",e.hash="",t&&(e.pathname="",e.search=""),e)}function ai(e){if(!(e instanceof URL))return!1;if(e.href==="about:blank"||e.href==="about:srcdoc"||e.protocol==="data:"||e.protocol==="file:")return!0;return t(e.origin);function t(s){if(s==null||s==="null")return!1;let r=new URL(s);return!!(r.protocol==="https:"||r.protocol==="wss:"||/^127(?:\.[0-9]+){0,2}\.[0-9]+$|^\[(?:0*:)*?:?0*1\]$/.test(r.hostname)||r.hostname==="localhost"||r.hostname.includes("localhost.")||r.hostname.endsWith(".localhost"))}}function Cx(e,t){if(Oo===void 0)return!0;let s=ph(t);if(s==="no metadata"||s.length===0)return!0;let r=wx(s),i=bx(s,r);for(let o of i){let n=o.algo,a=o.hash,A=Oo.createHash(n).update(e).digest("base64");if(A[A.length-1]==="="&&(A[A.length-2]==="="?A=A.slice(0,-2):A=A.slice(0,-1)),yx(A,a))return!0}return!1}var Ix=/(?<algo>sha256|sha384|sha512)-((?<hash>[A-Za-z0-9+/]+|[A-Za-z0-9_-]+)={0,2}(?:\s|$)( +[!-~]*)?)?/i;function ph(e){let t=[],s=!0;for(let r of e.split(" ")){s=!1;let i=Ix.exec(r);if(i===null||i.groups===void 0||i.groups.algo===void 0)continue;let o=i.groups.algo.toLowerCase();ah.includes(o)&&t.push(i.groups)}return s===!0?"no metadata":t}function wx(e){let t=e[0].algo;if(t[3]==="5")return t;for(let s=1;s<e.length;++s){let r=e[s];if(r.algo[3]==="5"){t="sha512";break}else{if(t[3]==="3")continue;r.algo[3]==="3"&&(t="sha384")}}return t}function bx(e,t){if(e.length===1)return e;let s=0;for(let r=0;r<e.length;++r)e[r].algo===t&&(e[s++]=e[r]);return e.length=s,e}function yx(e,t){if(e.length!==t.length)return!1;for(let s=0;s<e.length;++s)if(e[s]!==t[s]){if(e[s]==="+"&&t[s]==="-"||e[s]==="/"&&t[s]==="_")continue;return!1}return!0}function xx(e){}function Jo(e,t){return e.origin===t.origin&&e.origin==="null"||e.protocol===t.protocol&&e.hostname===t.hostname&&e.port===t.port}function vx(){let e,t;return{promise:new Promise((r,i)=>{e=r,t=i}),resolve:e,reject:t}}function kx(e){return e.controller.state==="aborted"}function Dx(e){return e.controller.state==="aborted"||e.controller.state==="terminated"}function Rx(e){return sx[e.toLowerCase()]??e}function Tx(e){let t=JSON.stringify(e);if(t===void 0)throw new TypeError("Value is not JSON serializable");return ws(typeof t=="string"),t}var Fx=Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));function gh(e,t,s=0,r=1){class i{#e;#t;#i;constructor(n,a){this.#e=n, Line truncated `)||e.includes("\r")||e.includes("\0"))===!1}function fv(e,t){let{headersList:s}=t,i=(s.get("referrer-policy",!0)??"").split(","),r="";if(i.length>0)for(let n=i.length;n!==0;n--){let o=i[n-1].trim();if(sv.has(o)){r=o;break}}r!==""&&(e.referrerPolicy=r)}function Qv(){return"allowed"}function Bv(){return"success"}function Cv(){return"success"}function Iv(e){let t=null;t=e.mode,e.headersList.set("sec-fetch-mode",t,!0)}function wv(e){let t=e.origin;if(!(t==="client"||t===void 0)){if(e.responseTainting==="cors"||e.mode==="websocket")e.headersList.append("origin",t,!0);else if(e.method!=="GET"&&e.method!=="HEAD"){switch(e.referrerPolicy){case"no-referrer":t=null;break;case"no-referrer-when-downgrade":case"strict-origin":case"strict-origin-when-cross-origin":e.origin&&Bc(e.origin)&&!Bc(mr(e))&&(t=null);break;case"same-origin":io(e,mr(e))||(t=null);break;default:}e.headersList.append("origin",t,!0)}}}function Ei(e,t){return e}function bv(e,t,s){return!e?.startTime||e.startTime<t?{domainLookupStartTime:t,domainLookupEndTime:t,connectionStartTime:t,connectionEndTime:t,secureConnectionStartTime:t,ALPNNegotiatedProtocol:e?.ALPNNegotiatedProtocol}:{domainLookupStartTime:Ei(e.domainLookupStartTime,s),domainLookupEndTime:Ei(e.domainLookupEndTime,s),connectionStartTime:Ei(e.connectionStartTime,s),connectionEndTime:Ei(e.connectionEndTime,s),secureConnectionStartTime:Ei(e.secureConnectionStartTime,s),ALPNNegotiatedProtocol:e.ALPNNegotiatedProtocol}}function yv(e){return Ei(av.now(),e)}function xv(e){return{startTime:e.startTime??0,redirectStartTime:0,redirectEndTime:0,postRedirectStartTime:e.startTime??0,finalServiceWorkerStartTime:0,finalNetworkResponseStartTime:0,finalNetworkRequestStartTime:0,endTime:0,encodedBodySize:0,decodedBodySize:0,finalConnectionTimingInfo:null}}function ed(){return{referrerPolicy:"strict-origin-when-cross-origin"}}function vv(e){return{referrerPolicy:e.referrerPolicy}}function kv(e){let t=e.referrerPolicy;bs(t);let s=null;if(e.referrer==="client"){let a=jh();if(!a||a.origin==="null")return"no-referrer";s=new URL(a)}else e.referrer instanceof URL&&(s=e.referrer);let i=Qc(s),r=Qc(s,!0);i.toString().length>4096&&(i=r);let n=io(e,i),o=Er(i)&&!Er(e.url);switch(t){case"origin":return r??Qc(s,!0);case"unsafe-url":return i;case"same-origin":return n?r:"no-referrer";case"origin-when-cross-origin":return n?i:r;case"strict-origin-when-cross-origin":{let a=mr(e);return io(i,a)?i:Er(i)&&!Er(a)?"no-referrer":r}default:return o?"no-referrer":r}}function Qc(e,t){return bs(e instanceof URL),e=new URL(e),e.protocol==="file:"||e.protocol==="about:"||e.protocol==="blank:"?"no-referrer":(e.username="",e.password="",e.hash="",t&&(e.pathname="",e.search=""),e)}function Er(e){if(!(e instanceof URL))return!1;if(e.href==="about:blank"||e.href==="about:srcdoc"||e.protocol==="data:"||e.protocol==="file:")return!0;return t(e.origin);function t(s){if(s==null||s==="null")return!1;let i=new URL(s);return!!(i.protocol==="https:"||i.protocol==="wss:"||/^127(?:\.[0-9]+){0,2}\.[0-9]+$|^\[(?:0*:)*?:?0*1\]$/.test(i.hostname)||i.hostname==="localhost"||i.hostname.includes("localhost.")||i.hostname.endsWith(".localhost"))}}function Dv(e,t){if(so===void 0)return!0;let s=td(t);if(s==="no metadata"||s.length===0)return!0;let i=Tv(s),r=Sv(s,i);for(let n of r){let o=n.algo,a=n.hash,A=so.createHash(o).update(e).digest("base64");if(A[A.length-1]==="="&&(A[A.length-2]==="="?A=A.slice(0,-2):A=A.slice(0,-1)),Fv(A,a))return!0}return!1}var Rv=/(?<algo>sha256|sha384|sha512)-((?<hash>[A-Za-z0-9+/]+|[A-Za-z0-9_-]+)={0,2}(?:\s|$)( +[!-~]*)?)?/i;function td(e){let t=[],s=!0;for(let i of e.split(" ")){s=!1;let r=Rv.exec(i);if(r===null||r.groups===void 0||r.groups.algo===void 0)continue;let n=r.groups.algo.toLowerCase();Zh.includes(n)&&t.push(r.groups)}return s===!0?"no metadata":t}function Tv(e){let t=e[0].algo;if(t[3]==="5")return t;for(let s=1;s<e.length;++s){let i=e[s];if(i.algo[3]==="5"){t="sha512";break}else{if(t[3]==="3")continue;i.algo[3]==="3"&&(t="sha384")}}return t}function Sv(e,t){if(e.length===1)return e;let s=0;for(let i=0;i<e.length;++i)e[i].algo===t&&(e[s++]=e[i]);return e.length=s,e}function Fv(e,t){if(e.length!==t.length)return!1;for(let s=0;s<e.length;++s)if(e[s]!==t[s]){if(e[s]==="+"&&t[s]==="-"||e[s]==="/"&&t[s]==="_")continue;return!1}return!0}function Uv(e){}function io(e,t){return e.origin===t.origin&&e.origin==="null"||e.protocol===t.protocol&&e.hostname===t.hostname&&e.port===t.port}function Nv(){let e,t;return{promise:new Promise((i,r)=>{e=i,t=r}),resolve:e,reject:t}}function Mv(e){return e.controller.state==="aborted"}function Gv(e){return e.controller.state==="aborted"||e.controller.state==="terminated"}function Lv(e){return lv[e.toLowerCase()]??e}function _v(e){let t=JSON.stringify(e);if(t===void 0)throw new TypeError("Value is not JSON serializable");return bs(typeof t=="string"),t}var Yv=Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));function sd(e,t,s=0,i=1){class r{#e;#t;#s;constructor(o,a){this.#e=o, Line truncated
`);function Av(e){for(let t=0;t<e.length;++t)if((e.charCodeAt(t)&-128)!==0)return!1;return!0}function cv(e){let t=e.length;if(t<27||t>70)return!1;for(let s=0;s<t;++s){let r=e.charCodeAt(s);if(!(r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r===39||r===45||r===95))return!1}return!0}function lv(e,t){Ho(t!=="failure"&&t.essence==="multipart/form-data");let s=t.parameters.get("boundary");if(s===void 0)return"failure";let r=Buffer.from(`--${s}`,"utf8"),i=[],o={position:0};for(;e[o.position]===13&&e[o.position+1]===10;)o.position+=2;let n=e.length;for(;e[n-1]===10&&e[n-2]===13;)n-=2;for(n!==e.length&&(e=e.subarray(0,n));;){if(e.subarray(o.position,o.position+r.length).equals(r))o.position+=r.length;else return"failure";if(o.position===e.length-2&&Vo(e,nv,o)||o.position===e.length-4&&Vo(e,av,o))return i;if(e[o.position]!==13||e[o.position+1]!==10)return"failure";o.position+=2;let a=uv(e,o);if(a==="failure")return"failure";let{name:A,filename:c,contentType:u,encoding:l}=a;o.position+=2;let p;{let h=e.indexOf(r.subarray(2),o.position);if(h===-1)return"failure";p=e.subarray(o.position,h-4),o.position+=p.length,l==="base64"&&(p=Buffer.from(p.toString(),"base64"))}if(e[o.position]!==13||e[o.position+1]!==10)return"failure";o.position+=2;let g;c!==null?(u??="text/plain",Av(u)||(u=""),g=new iv([p],c,{type:u})):g=$x(Buffer.from(p)),Ho(yh(A)),Ho(typeof g=="string"&&yh(g)||tv(g)),i.push(sv(A,g,c))}}function uv(e,t){let s=null,r=null,i=null,o=null;for(;;){if(e[t.position]===13&&e[t.position+1]===10)return s===null?"failure":{name:s,filename:r,contentType:i,encoding:o};let n=cr(a=>a!==10&&a!==13&&a!==58,e,t);if(n=oc(n,!0,!0,a=>a===9||a===32),!ev.test(n.toString())||e[t.position]!==58)return"failure";switch(t.position++,cr(a=>a===32||a===9,e,t),Xx(n)){case"content-disposition":{if(s=r=null,!Vo(e,ov,t)||(t.position+=17,s=kh(e,t),s===null))return"failure";if(Vo(e,vh,t)){let a=t.position+vh.length;if(e[a]===42&&(t.position+=1,a+=1),e[a]!==61||e[a+1]!==34||(t.position+=12,r=kh(e,t),r===null))return"failure"}break}case"content-type":{let a=cr(A=>A!==10&&A!==13,e,t);a=oc(a,!1,!0,A=>A===9||A===32),i=xh(a);break}case"content-transfer-encoding":{let a=cr(A=>A!==10&&A!==13,e,t);a=oc(a,!1,!0,A=>A===9||A===32),o=xh(a);break}default:cr(a=>a!==10&&a!==13,e,t)}if(e[t.position]!==13&&e[t.position+1]!==10)return"failure";t.position+=2}}function kh(e,t){Ho(e[t.position-1]===34);let s=cr(r=>r!==10&&r!==13&&r!==34,e,t);return e[t.position]!==34?null:(t.position++,s=new TextDecoder().decode(s).replace(/%0A/ig,` `);function Ek(e){for(let t=0;t<e.length;++t)if((e.charCodeAt(t)&-128)!==0)return!1;return!0}function mk(e){let t=e.length;if(t<27||t>70)return!1;for(let s=0;s<t;++s){let i=e.charCodeAt(s);if(!(i>=48&&i<=57||i>=65&&i<=90||i>=97&&i<=122||i===39||i===45||i===95))return!1}return!0}function fk(e,t){no(t!=="failure"&&t.essence==="multipart/form-data");let s=t.parameters.get("boundary");if(s===void 0)return"failure";let i=Buffer.from(`--${s}`,"utf8"),r=[],n={position:0};for(;e[n.position]===13&&e[n.position+1]===10;)n.position+=2;let o=e.length;for(;e[o-1]===10&&e[o-2]===13;)o-=2;for(o!==e.length&&(e=e.subarray(0,o));;){if(e.subarray(n.position,n.position+i.length).equals(i))n.position+=i.length;else return"failure";if(n.position===e.length-2&&oo(e,hk,n)||n.position===e.length-4&&oo(e,dk,n))return r;if(e[n.position]!==13||e[n.position+1]!==10)return"failure";n.position+=2;let a=Qk(e,n);if(a==="failure")return"failure";let{name:A,filename:c,contentType:l,encoding:u}=a;n.position+=2;let p;{let d=e.indexOf(i.subarray(2),n.position);if(d===-1)return"failure";p=e.subarray(n.position,d-4),n.position+=p.length,u==="base64"&&(p=Buffer.from(p.toString(),"base64"))}if(e[n.position]!==13||e[n.position+1]!==10)return"failure";n.position+=2;let h;c!==null?(l??="text/plain",Ek(l)||(l=""),h=new pk([p],c,{type:l})):h=ak(Buffer.from(p)),no(hd(A)),no(typeof h=="string"&&hd(h)||ck(h)),r.push(lk(A,h,c))}}function Qk(e,t){let s=null,i=null,r=null,n=null;for(;;){if(e[t.position]===13&&e[t.position+1]===10)return s===null?"failure":{name:s,filename:i,contentType:r,encoding:n};let o=fi(a=>a!==10&&a!==13&&a!==58,e,t);if(o=vc(o,!0,!0,a=>a===9||a===32),!Ak.test(o.toString())||e[t.position]!==58)return"failure";switch(t.position++,fi(a=>a===32||a===9,e,t),ok(o)){case"content-disposition":{if(s=i=null,!oo(e,gk,t)||(t.position+=17,s=md(e,t),s===null))return"failure";if(oo(e,Ed,t)){let a=t.position+Ed.length;if(e[a]===42&&(t.position+=1,a+=1),e[a]!==61||e[a+1]!==34||(t.position+=12,i=md(e,t),i===null))return"failure"}break}case"content-type":{let a=fi(A=>A!==10&&A!==13,e,t);a=vc(a,!1,!0,A=>A===9||A===32),r=dd(a);break}case"content-transfer-encoding":{let a=fi(A=>A!==10&&A!==13,e,t);a=vc(a,!1,!0,A=>A===9||A===32),n=dd(a);break}default:fi(a=>a!==10&&a!==13,e,t)}if(e[t.position]!==13&&e[t.position+1]!==10)return"failure";t.position+=2}}function md(e,t){no(e[t.position-1]===34);let s=fi(i=>i!==10&&i!==13&&i!==34,e,t);return e[t.position]!==34?null:(t.position++,s=new TextDecoder().decode(s).replace(/%0A/ig,`
`).replace(/%0D/ig,"\r").replace(/%22/g,'"'),s)}function cr(e,t,s){let r=s.position;for(;r<t.length&&e(t[r]);)++r;return t.subarray(s.position,s.position=r)}function oc(e,t,s,r){let i=0,o=e.length-1;if(t)for(;i<e.length&&r(e[i]);)i++;if(s)for(;o>0&&r(e[o]);)o--;return i===0&&o===e.length-1?e:e.subarray(i,o+1)}function Vo(e,t,s){if(e.length<t.length)return!1;for(let r=0;r<t.length;r++)if(t[r]!==e[s.position+r])return!1;return!0}Dh.exports={multipartFormDataParser:lv,validateBoundary:cv}});var pr=Q((v2,Yh)=>{"use strict";var ui=U(),{ReadableStreamFrom:pv,isBlobLike:Th,isReadableStreamLike:gv,readableStreamClose:hv,createDeferredPromise:dv,fullyReadBody:Ev,extractMimeType:mv,utf8DecodeBytes:Uh}=Ue(),{FormData:Fh}=li(),{kState:ur}=Kt(),{webidl:fv}=ge(),{Blob:Qv}=require("node:buffer"),nc=require("node:assert"),{isErrored:Nh,isDisturbed:Bv}=require("node:stream"),{isArrayBuffer:Cv}=require("node:util/types"),{serializeAMimeType:Iv}=ve(),{multipartFormDataParser:wv}=Rh(),ac;try{let e=require("node:crypto");ac=t=>e.randomInt(0,t)}catch{ac=e=>Math.floor(Math.random(e))}var qo=new TextEncoder;function bv(){}var Gh=globalThis.FinalizationRegistry&&process.version.indexOf("v18")!==0,Mh;Gh&&(Mh=new FinalizationRegistry(e=>{let t=e.deref();t&&!t.locked&&!Bv(t)&&!Nh(t)&&t.cancel("Response object has been garbage collected").catch(bv)}));function Lh(e,t=!1){let s=null;e instanceof ReadableStream?s=e:Th(e)?s=e.stream():s=new ReadableStream({async pull(A){let c=typeof i=="string"?qo.encode(i):i;c.byteLength&&A.enqueue(c),queueMicrotask(()=>hv(A))},start(){},type:"bytes"}),nc(gv(s));let r=null,i=null,o=null,n=null;if(typeof e=="string")i=e,n="text/plain;charset=UTF-8";else if(e instanceof URLSearchParams)i=e.toString(),n="application/x-www-form-urlencoded;charset=UTF-8";else if(Cv(e))i=new Uint8Array(e.slice());else if(ArrayBuffer.isView(e))i=new Uint8Array(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength));else if(ui.isFormDataLike(e)){let A=`----formdata-undici-0${`${ac(1e11)}`.padStart(11,"0")}`,c=`--${A}\r `).replace(/%0D/ig,"\r").replace(/%22/g,'"'),s)}function fi(e,t,s){let i=s.position;for(;i<t.length&&e(t[i]);)++i;return t.subarray(s.position,s.position=i)}function vc(e,t,s,i){let r=0,n=e.length-1;if(t)for(;r<e.length&&i(e[r]);)r++;if(s)for(;n>0&&i(e[n]);)n--;return r===0&&n===e.length-1?e:e.subarray(r,n+1)}function oo(e,t,s){if(e.length<t.length)return!1;for(let i=0;i<t.length;i++)if(t[i]!==e[s.position+i])return!1;return!0}fd.exports={multipartFormDataParser:fk,validateBoundary:mk}});var Ci=Q((OY,Dd)=>{"use strict";var Br=N(),{ReadableStreamFrom:Bk,isBlobLike:Bd,isReadableStreamLike:Ck,readableStreamClose:Ik,createDeferredPromise:wk,fullyReadBody:bk,extractMimeType:yk,utf8DecodeBytes:wd}=Ge(),{FormData:Cd}=Qr(),{kState:Bi}=Kt(),{webidl:xk}=Ee(),{Blob:vk}=require("node:buffer"),kc=require("node:assert"),{isErrored:bd,isDisturbed:kk}=require("node:stream"),{isArrayBuffer:Dk}=require("node:util/types"),{serializeAMimeType:Rk}=De(),{multipartFormDataParser:Tk}=Qd(),Dc;try{let e=require("node:crypto");Dc=t=>e.randomInt(0,t)}catch{Dc=e=>Math.floor(Math.random(e))}var ao=new TextEncoder;function Sk(){}var yd=globalThis.FinalizationRegistry&&process.version.indexOf("v18")!==0,xd;yd&&(xd=new FinalizationRegistry(e=>{let t=e.deref();t&&!t.locked&&!kk(t)&&!bd(t)&&t.cancel("Response object has been garbage collected").catch(Sk)}));function vd(e,t=!1){let s=null;e instanceof ReadableStream?s=e:Bd(e)?s=e.stream():s=new ReadableStream({async pull(A){let c=typeof r=="string"?ao.encode(r):r;c.byteLength&&A.enqueue(c),queueMicrotask(()=>Ik(A))},start(){},type:"bytes"}),kc(Ck(s));let i=null,r=null,n=null,o=null;if(typeof e=="string")r=e,o="text/plain;charset=UTF-8";else if(e instanceof URLSearchParams)r=e.toString(),o="application/x-www-form-urlencoded;charset=UTF-8";else if(Dk(e))r=new Uint8Array(e.slice());else if(ArrayBuffer.isView(e))r=new Uint8Array(e.buffer.slice(e.byteOffset,e.byteOffset+e.byteLength));else if(Br.isFormDataLike(e)){let A=`----formdata-undici-0${`${Dc(1e11)}`.padStart(11,"0")}`,c=`--${A}\r
Content-Disposition: form-data`;let u=m=>m.replace(/\n/g,"%0A").replace(/\r/g,"%0D").replace(/"/g,"%22"),l=m=>m.replace(/\r?\n|\r/g,`\r Content-Disposition: form-data`;let l=f=>f.replace(/\n/g,"%0A").replace(/\r/g,"%0D").replace(/"/g,"%22"),u=f=>f.replace(/\r?\n|\r/g,`\r
`),p=[],g=new Uint8Array([13,10]);o=0;let h=!1;for(let[m,d]of e)if(typeof d=="string"){let f=qo.encode(c+`; name="${u(l(m))}"\r `),p=[],h=new Uint8Array([13,10]);n=0;let d=!1;for(let[f,g]of e)if(typeof g=="string"){let m=ao.encode(c+`; name="${l(u(f))}"\r
\r \r
${l(d)}\r ${u(g)}\r
`);p.push(f),o+=f.byteLength}else{let f=qo.encode(`${c}; name="${u(l(m))}"`+(d.name?`; filename="${u(d.name)}"`:"")+`\r `);p.push(m),n+=m.byteLength}else{let m=ao.encode(`${c}; name="${l(u(f))}"`+(g.name?`; filename="${l(g.name)}"`:"")+`\r
Content-Type: ${d.type||"application/octet-stream"}\r Content-Type: ${g.type||"application/octet-stream"}\r
\r \r
`);p.push(f,d,g),typeof d.size=="number"?o+=f.byteLength+d.size+g.byteLength:h=!0}let E=qo.encode(`--${A}--\r `);p.push(m,g,h),typeof g.size=="number"?n+=m.byteLength+g.size+h.byteLength:d=!0}let E=ao.encode(`--${A}--\r
`);p.push(E),o+=E.byteLength,h&&(o=null),i=e,r=async function*(){for(let m of p)m.stream?yield*m.stream():yield m},n=`multipart/form-data; boundary=${A}`}else if(Th(e))i=e,o=e.size,e.type&&(n=e.type);else if(typeof e[Symbol.asyncIterator]=="function"){if(t)throw new TypeError("keepalive");if(ui.isDisturbed(e)||e.locked)throw new TypeError("Response body object should not be disturbed or locked");s=e instanceof ReadableStream?e:pv(e)}if((typeof i=="string"||ui.isBuffer(i))&&(o=Buffer.byteLength(i)),r!=null){let A;s=new ReadableStream({async start(){A=r(e)[Symbol.asyncIterator]()},async pull(c){let{value:u,done:l}=await A.next();if(l)queueMicrotask(()=>{c.close(),c.byobRequest?.respond(0)});else if(!Nh(s)){let p=new Uint8Array(u);p.byteLength&&c.enqueue(p)}return c.desiredSize>0},async cancel(c){await A.return()},type:"bytes"})}return[{stream:s,source:i,length:o},n]}function yv(e,t=!1){return e instanceof ReadableStream&&(nc(!ui.isDisturbed(e),"The body has already been consumed."),nc(!e.locked,"The stream is locked.")),Lh(e,t)}function xv(e,t){let[s,r]=t.stream.tee();return t.stream=s,{stream:r,length:t.length,source:t.source}}function vv(e){if(e.aborted)throw new DOMException("The operation was aborted.","AbortError")}function kv(e){return{blob(){return lr(this,s=>{let r=Sh(this);return r===null?r="":r&&(r=Iv(r)),new Qv([s],{type:r})},e)},arrayBuffer(){return lr(this,s=>new Uint8Array(s).buffer,e)},text(){return lr(this,Uh,e)},json(){return lr(this,Rv,e)},formData(){return lr(this,s=>{let r=Sh(this);if(r!==null)switch(r.essence){case"multipart/form-data":{let i=wv(s,r);if(i==="failure")throw new TypeError("Failed to parse body as FormData.");let o=new Fh;return o[ur]=i,o}case"application/x-www-form-urlencoded":{let i=new URLSearchParams(s.toString()),o=new Fh;for(let[n,a]of i)o.append(n,a);return o}}throw new TypeError('Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".')},e)},bytes(){return lr(this,s=>new Uint8Array(s),e)}}}function Dv(e){Object.assign(e.prototype,kv(e))}async function lr(e,t,s){if(fv.brandCheck(e,s),_h(e))throw new TypeError("Body is unusable: Body has already been read");vv(e[ur]);let r=dv(),i=n=>r.reject(n),o=n=>{try{r.resolve(t(n))}catch(a){i(a)}};return e[ur].body==null?(o(Buffer.allocUnsafe(0)),r.promise):(await Ev(e[ur].body,o,i),r.promise)}function _h(e){let t=e[ur].body;return t!=null&&(t.stream.locked||ui.isDisturbed(t.stream))}function Rv(e){return JSON.parse(Uh(e))}function Sh(e){let t=e[ur].headersList,s=mv(t);return s==="failure"?null:s}Yh.exports={extractBody:Lh,safelyExtractBody:yv,cloneBody:xv,mixinBody:Dv,streamRegistry:Mh,hasFinalizationRegistry:Gh,bodyUnusable:_h}});var Kh=Q((k2,Zh)=>{"use strict";var k=require("node:assert"),F=U(),{channels:Oh}=$s(),Ac=PA(),{RequestContentLengthMismatchError:bs,ResponseContentLengthMismatchError:Tv,RequestAbortedError:Wh,HeadersTimeoutError:Fv,HeadersOverflowError:Sv,SocketError:Xo,InformationalError:gr,BodyTimeoutError:Uv,HTTPParserError:Nv,ResponseExceededMaxSizeError:Gv}=L(),{kUrl:jh,kReset:ke,kClient:pc,kParser:X,kBlocking:hi,kRunning:Ee,kPending:Mv,kSize:Jh,kWriting:$t,kQueue:st,kNoRef:pi,kKeepAliveDefaultTimeout:Lv,kHostHeader:_v,kPendingIdx:Yv,kRunningIdx:qe,kError:We,kPipelining:Zo,kSocket:hr,kKeepAliveTimeoutValue:$o,kMaxHeadersSize:cc,kKeepAliveMaxTimeout:Ov,kKeepAliveTimeoutThreshold:Jv,kHeadersTimeout:Pv,kBodyTimeout:Hv,kStrictContentLength:gc,kMaxRequests:Ph,kCounter:Vv,kMaxResponseSize:qv,kOnError:Wv,kResume:Xt,kHTTPContext:zh}=j(),ft=Mg(),jv=Buffer.alloc(0),Wo=Buffer[Symbol.species],jo=F.addListener,zv=F.removeAllListeners,lc;async function Zv(){let e=process.env.JEST_WORKER_ID?qA():void 0,t;try{t=await WebAssembly.compile(Yg())}catch{t=await WebAssembly.compile(e||qA())}return await WebAssembly.instantiate(t,{env:{wasm_on_url:(s,r,i)=>0,wasm_on_status:(s,r,i)=>{k(oe.ptr===s);let o=r-Bt+Qt.byteOffset;return oe.onStatus(new Wo(Qt.buffer,o,i))||0},wasm_on_message_begin:s=>(k(oe.ptr===s),oe.onMessageBegin()||0),wasm_on_header_field:(s,r,i)=>{k(oe.ptr===s);let o=r-Bt+Qt.byteOffset;return oe.onHeaderField(new Wo(Qt.buffer,o,i))||0},wasm_on_header_value:(s,r,i)=>{k(oe.ptr===s);let o=r-Bt+Qt.byteOffset;return oe.onHeaderValue(new Wo(Qt.buffer,o,i))||0},wasm_on_headers_complete:(s,r,i,o)=>(k(oe.ptr===s),oe.onHeadersComplete(r,!!i,!!o)||0),wasm_on_body:(s,r,i)=>{k(oe.ptr===s);let o=r-Bt+Qt.byteOffset;return oe.onBody(new Wo(Qt.buffer,o,i))||0},wasm_on_message_complete:s=>(k(oe.ptr===s),oe.onMessageComplete()||0)}})}var uc=null,hc=Zv();hc.catch();var oe=null,Qt=null,zo=0,Bt=null,Kv=0,gi=1,dr=2|gi,Ko=4|gi,dc=8|Kv,Ec=class{constructor(t,s,{exports:r}){k(Number.isFinite(t[cc])&&t[cc]>0),this.llhttp=r,this.ptr=this.llhttp.llhttp_alloc(ft.TYPE.RESPONSE),this.client=t,this.socket=s,this.timeout=null,this.timeoutValue=null,this.timeoutType=null,this.statusCode=null,this.statusText="",this.upgrade=!1,this.headers=[],this.headersSize=0,this.headersMaxSize=t[cc],this.shouldKeepAlive=!1,this.paused=!1, Line truncated `);p.push(E),n+=E.byteLength,d&&(n=null),r=e,i=async function*(){for(let f of p)f.stream?yield*f.stream():yield f},o=`multipart/form-data; boundary=${A}`}else if(Bd(e))r=e,n=e.size,e.type&&(o=e.type);else if(typeof e[Symbol.asyncIterator]=="function"){if(t)throw new TypeError("keepalive");if(Br.isDisturbed(e)||e.locked)throw new TypeError("Response body object should not be disturbed or locked");s=e instanceof ReadableStream?e:Bk(e)}if((typeof r=="string"||Br.isBuffer(r))&&(n=Buffer.byteLength(r)),i!=null){let A;s=new ReadableStream({async start(){A=i(e)[Symbol.asyncIterator]()},async pull(c){let{value:l,done:u}=await A.next();if(u)queueMicrotask(()=>{c.close(),c.byobRequest?.respond(0)});else if(!bd(s)){let p=new Uint8Array(l);p.byteLength&&c.enqueue(p)}return c.desiredSize>0},async cancel(c){await A.return()},type:"bytes"})}return[{stream:s,source:r,length:n},o]}function Fk(e,t=!1){return e instanceof ReadableStream&&(kc(!Br.isDisturbed(e),"The body has already been consumed."),kc(!e.locked,"The stream is locked.")),vd(e,t)}function Uk(e,t){let[s,i]=t.stream.tee();return t.stream=s,{stream:i,length:t.length,source:t.source}}function Nk(e){if(e.aborted)throw new DOMException("The operation was aborted.","AbortError")}function Mk(e){return{blob(){return Qi(this,s=>{let i=Id(this);return i===null?i="":i&&(i=Rk(i)),new vk([s],{type:i})},e)},arrayBuffer(){return Qi(this,s=>new Uint8Array(s).buffer,e)},text(){return Qi(this,wd,e)},json(){return Qi(this,Lk,e)},formData(){return Qi(this,s=>{let i=Id(this);if(i!==null)switch(i.essence){case"multipart/form-data":{let r=Tk(s,i);if(r==="failure")throw new TypeError("Failed to parse body as FormData.");let n=new Cd;return n[Bi]=r,n}case"application/x-www-form-urlencoded":{let r=new URLSearchParams(s.toString()),n=new Cd;for(let[o,a]of r)n.append(o,a);return n}}throw new TypeError('Content-Type was not one of "multipart/form-data" or "application/x-www-form-urlencoded".')},e)},bytes(){return Qi(this,s=>new Uint8Array(s),e)}}}function Gk(e){Object.assign(e.prototype,Mk(e))}async function Qi(e,t,s){if(xk.brandCheck(e,s),kd(e))throw new TypeError("Body is unusable: Body has already been read");Nk(e[Bi]);let i=wk(),r=o=>i.reject(o),n=o=>{try{i.resolve(t(o))}catch(a){r(a)}};return e[Bi].body==null?(n(Buffer.allocUnsafe(0)),i.promise):(await bk(e[Bi].body,n,r),i.promise)}function kd(e){let t=e[Bi].body;return t!=null&&(t.stream.locked||Br.isDisturbed(t.stream))}function Lk(e){return JSON.parse(wd(e))}function Id(e){let t=e[Bi].headersList,s=yk(t);return s==="failure"?null:s}Dd.exports={extractBody:vd,safelyExtractBody:Fk,cloneBody:Uk,mixinBody:Gk,streamRegistry:xd,hasFinalizationRegistry:yd,bodyUnusable:kd}});var Vd=Q((PY,Hd)=>{"use strict";var D=require("node:assert"),x=N(),{channels:Rd}=Ai(),Rc=uc(),{RequestContentLengthMismatchError:ys,ResponseContentLengthMismatchError:Td,RequestAbortedError:_d,InvalidArgumentError:_k,HeadersTimeoutError:Yk,HeadersOverflowError:Ok,SocketError:Ii,InformationalError:xs,BodyTimeoutError:Pk,HTTPParserError:Jk,ResponseExceededMaxSizeError:Hk}=Y(),{kUrl:Yd,kReset:Re,kClient:uo,kParser:j,kBlocking:wr,kRunning:oe,kPending:Od,kSize:Sd,kWriting:Xt,kQueue:rt,kNoRef:Cr,kKeepAliveDefaultTimeout:Vk,kHostHeader:qk,kPendingIdx:Wk,kRunningIdx:Ke,kError:be,kPipelining:po,kSocket:vs,kKeepAliveTimeoutValue:ho,kMaxHeadersSize:Tc,kKeepAliveMaxTimeout:jk,kKeepAliveTimeoutThreshold:zk,kHeadersTimeout:Zk,kBodyTimeout:Kk,kStrictContentLength:Uc,kMaxRequests:Fd,kCounter:Xk,kMaxResponseSize:$k,kOnError:Ud,kResume:Lt,kHTTPContext:Pd}=Z(),Le=xh(),Nd=Buffer.alloc(0),Ao=Buffer[Symbol.species],co=x.addListener,e0=x.removeAllListeners,ks=Symbol("kIdleSocketValidation"),wi=Symbol("kIdleSocketValidationTimeout"),Lc=Symbol("kSocketUsed"),Sc;async function t0(){let e=process.env.JEST_WORKER_ID?hc():void 0,t;try{t=await WebAssembly.compile(Dh())}catch{t=await WebAssembly.compile(e||hc())}return await WebAssembly.instantiate(t,{env:{wasm_on_url:(s,i,r)=>0,wasm_on_status:(s,i,r)=>{D(ee.ptr===s);let n=i-Qt+ft.byteOffset;return ee.onStatus(new Ao(ft.buffer,n,r))||0},wasm_on_message_begin:s=>(D(ee.ptr===s),ee.onMessageBegin()||0),wasm_on_header_field:(s,i,r)=>{D(ee.ptr===s);let n=i-Qt+ft.byteOffset;return ee.onHeaderField(new Ao(ft.buffer,n,r))||0},wasm_on_header_value:(s,i,r)=>{D(ee.ptr===s);let n=i-Qt+ft.byteOffset;return ee.onHeaderValue(new Ao(ft.buffer,n,r))||0},wasm_on_headers_complete:(s,i,r,n)=>(D(ee.ptr===s),ee.onHeadersComplete(i,!!r,!!n)||0),wasm_on_body:(s,i,r)=>{D(ee.ptr===s);let n=i-Qt+ft.byteOffset;return ee.onBody(new Ao(ft.buffer,n,r))||0},wasm_on_message_complete:s=>(D(ee.ptr===s),ee.onMessageComplete()||0)}})}var Fc=null,Nc=t0();Nc.catch();var ee=null,ft=null,lo=0,Qt=null,s0=0,Ir=1,bi=2|Ir,go=4|Ir,Mc=8|s0,Gc=class{constructor(t,s,{exports:i}){D(Number.isFinite(t[Tc])&&t[Tc]>0),this.llhttp=i,this.ptr=this.llhttp.llhttp_alloc(Le.TYPE.RESPONSE),this.client=t,this.socket=s,this.timeout=null,this.timeoutValue=null,this.timeoutType=null,this.statusCode=null,this.stat Line truncated
`;if(typeof i=="string"?E+=`host: ${i}\r `;if(typeof r=="string"?E+=`host: ${r}\r
`:E+=e[_v],o?E+=`connection: upgrade\r `:E+=e[qk],n?E+=`connection: upgrade\r
upgrade: ${o}\r upgrade: ${n}\r
`:e[Zo]&&!g[ke]?E+=`connection: keep-alive\r `:e[po]&&!h[Re]?E+=`connection: keep-alive\r
`:E+=`connection: close\r `:E+=`connection: close\r
`,Array.isArray(c))for(let m=0;m<c.length;m+=2){let d=c[m+0],f=c[m+1];if(Array.isArray(f))for(let C=0;C<f.length;C++)E+=`${d}: ${f[C]}\r `,Array.isArray(c))for(let f=0;f<c.length;f+=2){let g=c[f+0],m=c[f+1];if(Array.isArray(m))for(let B=0;B<m.length;B++)E+=`${g}: ${m[B]}\r
`;else E+=`${d}: ${f}\r `;else E+=`${g}: ${m}\r
`}return Oh.sendHeaders.hasSubscribers&&Oh.sendHeaders.publish({request:t,headers:E,socket:g}),!A||p===0?Vh(h,null,e,t,g,u,E,l):F.isBuffer(A)?Vh(h,A,e,t,g,u,E,l):F.isBlobLike(A)?typeof A.stream=="function"?qh(h,A.stream(),e,t,g,u,E,l):rk(h,A,e,t,g,u,E,l):F.isStream(A)?sk(h,A,e,t,g,u,E,l):F.isIterable(A)?qh(h,A,e,t,g,u,E,l):k(!1),!0}function sk(e,t,s,r,i,o,n,a){k(o!==0||s[Ee]===0,"stream body cannot be pipelined");let A=!1,c=new en({abort:e,socket:i,request:r,contentLength:o,client:s,expectsPayload:a,header:n}),u=function(h){if(!A)try{!c.write(h)&&this.pause&&this.pause()}catch(E){F.destroy(this,E)}},l=function(){A||t.resume&&t.resume()},p=function(){if(queueMicrotask(()=>{t.removeListener("error",g)}),!A){let h=new Wh;queueMicrotask(()=>g(h))}},g=function(h){if(!A){if(A=!0,k(i.destroyed||i[$t]&&s[Ee]<=1),i.off("drain",l).off("error",g),t.removeListener("data",u).removeListener("end",g).removeListener("close",p),!h)try{c.end()}catch(E){h=E}c.destroy(h),h&&(h.code!=="UND_ERR_INFO"||h.message!=="reset")?F.destroy(t,h):F.destroy(t)}};t.on("data",u).on("end",g).on("error",g).on("close",p),t.resume&&t.resume(),i.on("drain",l).on("error",g),t.errorEmitted??t.errored?setImmediate(()=>g(t.errored)):(t.endEmitted??t.readableEnded)&&setImmediate(()=>g(null)),(t.closeEmitted??t.closed)&&setImmediate(p)}function Vh(e,t,s,r,i,o,n,a){try{t?F.isBuffer(t)&&(k(o===t.byteLength,"buffer body must have content length"),i.cork(),i.write(`${n}content-length: ${o}\r `}return Rd.sendHeaders.hasSubscribers&&Rd.sendHeaders.publish({request:t,headers:E,socket:h}),!A||p===0?Gd(d,null,e,t,h,l,E,u):x.isBuffer(A)?Gd(d,A,e,t,h,l,E,u):x.isBlobLike(A)?typeof A.stream=="function"?Ld(d,A.stream(),e,t,h,l,E,u):c0(d,A,e,t,h,l,E,u):x.isStream(A)?A0(d,A,e,t,h,l,E,u):x.isIterable(A)?Ld(d,A,e,t,h,l,E,u):D(!1),!0}function A0(e,t,s,i,r,n,o,a){D(n!==0||s[oe]===0,"stream body cannot be pipelined");let A=!1,c=new Eo({abort:e,socket:r,request:i,contentLength:n,client:s,expectsPayload:a,header:o}),l=function(d){if(!A)try{!c.write(d)&&this.pause&&this.pause()}catch(E){x.destroy(this,E)}},u=function(){A||t.resume&&t.resume()},p=function(){if(queueMicrotask(()=>{t.removeListener("error",h)}),!A){let d=new _d;queueMicrotask(()=>h(d))}},h=function(d){if(!A){if(A=!0,D(r.destroyed||r[Xt]&&s[oe]<=1),r.off("drain",u).off("error",h),t.removeListener("data",l).removeListener("end",h).removeListener("close",p),!d)try{c.end()}catch(E){d=E}c.destroy(d),d&&(d.code!=="UND_ERR_INFO"||d.message!=="reset")?x.destroy(t,d):x.destroy(t)}};t.on("data",l).on("end",h).on("error",h).on("close",p),t.resume&&t.resume(),r.on("drain",u).on("error",h),t.errorEmitted??t.errored?setImmediate(()=>h(t.errored)):(t.endEmitted??t.readableEnded)&&setImmediate(()=>h(null)),(t.closeEmitted??t.closed)&&setImmediate(p)}function Gd(e,t,s,i,r,n,o,a){try{t?x.isBuffer(t)&&(D(n===t.byteLength,"buffer body must have content length"),r.cork(),r.write(`${o}content-length: ${n}\r
\r \r
`,"latin1"),i.write(t),i.uncork(),r.onBodySent(t),!a&&r.reset!==!1&&(i[ke]=!0)):o===0?i.write(`${n}content-length: 0\r `,"latin1"),r.write(t),r.uncork(),i.onBodySent(t),!a&&i.reset!==!1&&(r[Re]=!0)):n===0?r.write(`${o}content-length: 0\r
\r \r
`,"latin1"):(k(o===null,"no body must not have content length"),i.write(`${n}\r `,"latin1"):(D(n===null,"no body must not have content length"),r.write(`${o}\r
`,"latin1")),r.onRequestSent(),s[Xt]()}catch(A){e(A)}}async function rk(e,t,s,r,i,o,n,a){k(o===t.size,"blob body must have content length");try{if(o!=null&&o!==t.size)throw new bs;let A=Buffer.from(await t.arrayBuffer());i.cork(),i.write(`${n}content-length: ${o}\r `,"latin1")),i.onRequestSent(),s[Lt]()}catch(A){e(A)}}async function c0(e,t,s,i,r,n,o,a){D(n===t.size,"blob body must have content length");try{if(n!=null&&n!==t.size)throw new ys;let A=Buffer.from(await t.arrayBuffer());r.cork(),r.write(`${o}content-length: ${n}\r
\r \r
`,"latin1"),i.write(A),i.uncork(),r.onBodySent(A),r.onRequestSent(),!a&&r.reset!==!1&&(i[ke]=!0),s[Xt]()}catch(A){e(A)}}async function qh(e,t,s,r,i,o,n,a){k(o!==0||s[Ee]===0,"iterator body cannot be pipelined");let A=null;function c(){if(A){let p=A;A=null,p()}}let u=()=>new Promise((p,g)=>{k(A===null),i[We]?g(i[We]):A=p});i.on("close",c).on("drain",c);let l=new en({abort:e,socket:i,request:r,contentLength:o,client:s,expectsPayload:a,header:n});try{for await(let p of t){if(i[We])throw i[We];l.write(p)||await u()}l.end()}catch(p){l.destroy(p)}finally{i.off("close",c).off("drain",c)}}var en=class{constructor({abort:t,socket:s,request:r,contentLength:i,client:o,expectsPayload:n,header:a}){this.socket=s,this.request=r,this.contentLength=i,this.client=o,this.bytesWritten=0,this.expectsPayload=n,this.header=a,this.abort=t,s[$t]=!0}write(t){let{socket:s,request:r,contentLength:i,client:o,bytesWritten:n,expectsPayload:a,header:A}=this;if(s[We])throw s[We];if(s.destroyed)return!1;let c=Buffer.byteLength(t);if(!c)return!0;if(i!==null&&n+c>i){if(o[gc])throw new bs;process.emitWarning(new bs)}s.cork(),n===0&&(!a&&r.reset!==!1&&(s[ke]=!0),i===null?s.write(`${A}transfer-encoding: chunked\r `,"latin1"),r.write(A),r.uncork(),i.onBodySent(A),i.onRequestSent(),!a&&i.reset!==!1&&(r[Re]=!0),s[Lt]()}catch(A){e(A)}}async function Ld(e,t,s,i,r,n,o,a){D(n!==0||s[oe]===0,"iterator body cannot be pipelined");let A=null;function c(){if(A){let p=A;A=null,p()}}let l=()=>new Promise((p,h)=>{D(A===null),r[be]?h(r[be]):A=p});r.on("close",c).on("drain",c);let u=new Eo({abort:e,socket:r,request:i,contentLength:n,client:s,expectsPayload:a,header:o});try{for await(let p of t){if(r[be])throw r[be];u.write(p)||await l()}u.end()}catch(p){u.destroy(p)}finally{r.off("close",c).off("drain",c)}}var Eo=class{constructor({abort:t,socket:s,request:i,contentLength:r,client:n,expectsPayload:o,header:a}){this.socket=s,this.request=i,this.contentLength=r,this.client=n,this.bytesWritten=0,this.expectsPayload=o,this.header=a,this.abort=t,s[Xt]=!0}write(t){let{socket:s,request:i,contentLength:r,client:n,bytesWritten:o,expectsPayload:a,header:A}=this;if(s[be])throw s[be];if(s.destroyed)return!1;let c=Buffer.byteLength(t);if(!c)return!0;if(r!==null&&o+c>r){if(n[Uc])throw new ys;process.emitWarning(new ys)}s.cork(),o===0&&(!a&&i.reset!==!1&&(s[Re]=!0),r===null?s.write(`${A}transfer-encoding: chunked\r
`,"latin1"):s.write(`${A}content-length: ${i}\r `,"latin1"):s.write(`${A}content-length: ${r}\r
\r \r
`,"latin1")),i===null&&s.write(`\r `,"latin1")),r===null&&s.write(`\r
${c.toString(16)}\r ${c.toString(16)}\r
`,"latin1"),this.bytesWritten+=c;let u=s.write(t);return s.uncork(),r.onBodySent(t),u||s[X].timeout&&s[X].timeoutType===dr&&s[X].timeout.refresh&&s[X].timeout.refresh(),u}end(){let{socket:t,contentLength:s,client:r,bytesWritten:i,expectsPayload:o,header:n,request:a}=this;if(a.onRequestSent(),t[$t]=!1,t[We])throw t[We];if(!t.destroyed){if(i===0?o?t.write(`${n}content-length: 0\r `,"latin1"),this.bytesWritten+=c;let l=s.write(t);return s.uncork(),i.onBodySent(t),l||s[j].timeout&&s[j].timeoutType===bi&&s[j].timeout.refresh&&s[j].timeout.refresh(),l}end(){let{socket:t,contentLength:s,client:i,bytesWritten:r,expectsPayload:n,header:o,request:a}=this;if(a.onRequestSent(),t[Xt]=!1,t[be])throw t[be];if(!t.destroyed){if(r===0?n?t.write(`${o}content-length: 0\r
\r \r
`,"latin1"):t.write(`${n}\r `,"latin1"):t.write(`${o}\r
`,"latin1"):s===null&&t.write(`\r `,"latin1"):s===null&&t.write(`\r
0\r 0\r
\r \r
`,"latin1"),s!==null&&i!==s){if(r[gc])throw new bs;process.emitWarning(new bs)}t[X].timeout&&t[X].timeoutType===dr&&t[X].timeout.refresh&&t[X].timeout.refresh(),r[Xt]()}}destroy(t){let{socket:s,client:r,abort:i}=this;s[$t]=!1,t&&(k(r[Ee]<=1,"pipeline should only contain this request"),i(t))}};Zh.exports=Xv});var od=Q((D2,id)=>{"use strict";var je=require("node:assert"),{pipeline:ik}=require("node:stream"),N=U(),{RequestContentLengthMismatchError:mc,RequestAbortedError:Xh,SocketError:di,InformationalError:fc}=L(),{kUrl:tn,kReset:rn,kClient:Er,kRunning:on,kPending:ok,kQueue:es,kPendingIdx:Qc,kRunningIdx:rt,kError:ot,kSocket:ae,kStrictContentLength:nk,kOnError:Bc,kMaxConcurrentStreams:rd,kHTTP2Session:it,kResume:ts,kSize:ak,kHTTPContext:Ak}=j(),Gt=Symbol("open streams"),$h,ed=!1,sn;try{sn=require("node:http2")}catch{sn={constants:{}}}var{constants:{HTTP2_HEADER_AUTHORITY:ck,HTTP2_HEADER_METHOD:lk,HTTP2_HEADER_PATH:uk,HTTP2_HEADER_SCHEME:pk,HTTP2_HEADER_CONTENT_LENGTH:gk,HTTP2_HEADER_EXPECT:hk,HTTP2_HEADER_STATUS:dk}}=sn;function Ek(e){let t=[];for(let[s,r]of Object.entries(e))if(Array.isArray(r))for(let i of r)t.push(Buffer.from(s),Buffer.from(i));else t.push(Buffer.from(s),Buffer.from(r));return t}async function mk(e,t){e[ae]=t,ed||(ed=!0,process.emitWarning("H2 support is experimental, expect them to change at any time.",{code:"UNDICI-H2"}));let s=sn.connect(e[tn],{createConnection:()=>t,peerMaxConcurrentStreams:e[rd]});s[Gt]=0,s[Er]=e,s[ae]=t,N.addListener(s,"error",Qk),N.addListener(s,"frameError",Bk),N.addListener(s,"end",Ck),N.addListener(s,"goaway",Ik),N.addListener(s,"close",function(){let{[Er]:i}=this,{[ae]:o}=i,n=this[ae][ot]||this[ot]||new di("closed",N.getSocketInfo(o));if(i[it]=null,i.destroyed){je(i[ok]===0);let a=i[es].splice(i[rt]);for(let A=0;A<a.length;A++){let c=a[A];N.errorRequest(i,c,n)}}}),s.unref(),e[it]=s,t[it]=s,N.addListener(t,"error",function(i){je(i.code!=="ERR_TLS_CERT_ALTNAME_INVALID"),this[ot]=i,this[Er][Bc](i)}),N.addListener(t,"end",function(){N.destroy(this,new di("other side closed",N.getSocketInfo(this)))}),N.addListener(t,"close",function(){let i=this[ot]||new di("closed",N.getSocketInfo(this));e[ae]=null,this[it]!=null&&this[it].destroy(i),e[Qc]=e[rt],je(e[on]===0),e.emit("disconnect",e[tn],[e],i),e[ts]()});let r=!1;return t.on("close",()=>{r=!0}),{version:"h2",defaultPipelining:1/0,write(...i){return bk(e,...i)},resume(){fk(e)},destroy(i,o){r?queueMicrotask(o):t.destroy(i).on("close",o)},get destroyed(){return t.destroyed},busy(){return!1}}}function fk(e){let t=e[ae];t?.destroyed===!1&&(e[ak]===0&&e[rd]===0?(t.unref(),e[it].unref()):(t.ref(),e[it].ref()))}function Qk(e){je(e.code!=="ERR_TLS_CERT_ALTNAME_INVALID"),this[ae][ot]=e,this[Er][Bc](e)}function Bk(e,t,s){if(s===0){let r=new fc(`HTTP/2: "frameError" received - type ${e}, code ${t}`);this[ae][ot]=r,this[Er][Bc](r)}}function Ck(){let e=new di("other side closed",N.getSocketInfo(this[ae]));this.destroy(e),N.destroy(this[ae],e)}function Ik(e){let t=this[ot]||new di(`HTTP/2: "GOAWAY" frame received with code ${e}`,N.getSocketInfo(this)),s=this[Er];if(s[ae]=null,s[Ak]=null,this[it]!=null&&(this[it].destroy(t),this[it]=null),N.destroy(this[ae],t),s[rt]<s[es].length){let r=s[es][s[rt]];s[es][s[rt]++]=null,N.errorRequest(s,r,t),s[Qc]=s[rt]}je(s[on]===0),s.emit("disconnect",s[tn],[s],t),s[ts]()}function wk(e){return e!=="GET"&&e!=="HEAD"&&e!=="OPTIONS"&&e!=="TRACE"&&e!=="CONNECT"}function bk(e,t){let s=e[it],{method:r,path:i,host:o,upgrade:n,expectContinue:a,signal:A,headers:c}=t,{body:u}=t;if(n)return N.errorRequest(e,t,new Error("Upgrade not supported for H2")),!1;let l={};for(let B=0;B<c.length;B+=2){let b=c[B+0],Y=c[B+1];if(Array.isArray(Y))for(let O=0;O<Y.length;O++)l[b]?l[b]+=`,${Y[O]}`:l[b]=Y[O];else l[b]=Y}let p,{hostname:g,port:h}=e[tn];l[ck]=o||`${g}${h?`:${h}`:""}`,l[lk]=r;let E=B=>{t.aborted||t.completed||(B=B||new Xh,N.errorRequest(e,t,B),p!=null&&N.destroy(p,B),N.destroy(u,B),e[es][e[rt]++]=null,e[ts]())};try{t.onConnect(E)}catch(B){N.errorRequest(e,t,B)}if(t.aborted)return!1;if(r==="CONNECT")return s.ref(),p=s.request(l,{endStream:!1,signal:A}),p.id&&!p.pending?(t.onUpgrade(null,null,p),++s[Gt],e[es][e[rt]++]=null):p.once("ready",()=>{t.onUpgrade(null,null,p),++s[Gt],e[es][e[rt]++]=null}),p.once("close",()=>{s[Gt]-=1,s[Gt]===0&&s.unref()}),!0;l[uk]=i,l[pk]="https";let m=r==="PUT"||r==="POST"||r==="PATCH";u&&typeof u.read=="function"&&u.read(0);let d=N.bodyLength(u);if(N.isFormDataLike(u)){$h??=pr().extractBody;let[B,b]=$h(u);l["content-type"]=b,u=B.stream,d=B.length}if(d==null&&(d=t.contentLength),(d===0||!m)&&(d=null),wk(r)&&d>0&&t.contentLength!=null&&t.contentLength!==d){if(e[nk])return N.errorRequest(e,t,new mc),!1;process.emitWarning(new mc)}d!=null&&(je(u,"no body must not have content length"),l[gk]=`${d}`),s.ref();let f=r==="GET"||r==="HEAD"||u===null;return a?(l[hk]="100-continue",p=s.request(l,{endStream:f,signal:A}),p.once("continue",C)):(p=s.request(l,{endStream:f,signal:A}),C()),++s[Gt],p.once("r Line truncated `,"latin1"),s!==null&&r!==s){if(i[Uc])throw new ys;process.emitWarning(new ys)}t[j].timeout&&t[j].timeoutType===bi&&t[j].timeout.refresh&&t[j].timeout.refresh(),i[Lt]()}}destroy(t){let{socket:s,client:i,abort:r}=this;s[Xt]=!1,t&&(D(i[oe]<=1,"pipeline should only contain this request"),r(t))}};Hd.exports=i0});var $d=Q((JY,Xd)=>{"use strict";var Ct=require("node:assert"),{errorMonitor:_c}=require("node:events"),{pipeline:l0}=require("node:stream"),M=N(),{RequestContentLengthMismatchError:Oc,RequestAbortedError:Yc,SocketError:br,InformationalError:mo}=Y(),{kUrl:fo,kReset:Bo,kClient:yi,kRunning:Co,kPending:u0,kQueue:Ds,kPendingIdx:Jc,kRunningIdx:Bt,kError:ot,kSocket:le,kStrictContentLength:p0,kOnError:Hc,kMaxConcurrentStreams:Zd,kHTTP2Session:nt,kResume:es,kSize:g0,kHTTPContext:h0}=Z(),$t=Symbol("open streams"),qd,Wd=!1,Qo;try{Qo=require("node:http2")}catch{Qo={constants:{}}}var{constants:{HTTP2_HEADER_AUTHORITY:d0,HTTP2_HEADER_METHOD:E0,HTTP2_HEADER_PATH:m0,HTTP2_HEADER_SCHEME:f0,HTTP2_HEADER_CONTENT_LENGTH:Q0,HTTP2_HEADER_EXPECT:B0,HTTP2_HEADER_STATUS:Pc}}=Qo;function Kd(e){let t=[];for(let[s,i]of Object.entries(e))if(Array.isArray(i))for(let r of i)t.push(Buffer.from(s),Buffer.from(r));else t.push(Buffer.from(s),Buffer.from(i));return t}function C0(e){let{[Pc]:t,...s}=e;return Kd(s)}async function I0(e,t){e[le]=t,Wd||(Wd=!0,process.emitWarning("H2 support is experimental, expect them to change at any time.",{code:"UNDICI-H2"}));let s=Qo.connect(e[fo],{createConnection:()=>t,peerMaxConcurrentStreams:e[Zd]});s[$t]=0,s[yi]=e,s[le]=t,M.addListener(s,"error",b0),M.addListener(s,"frameError",y0),M.addListener(s,"end",x0),M.addListener(s,"goaway",v0),M.addListener(s,"close",function(){let{[yi]:r}=this,{[le]:n}=r,o=this[le][ot]||this[ot]||new br("closed",M.getSocketInfo(n));if(r[nt]=null,r.destroyed){Ct(r[u0]===0);let a=r[Ds].splice(r[Bt]);for(let A=0;A<a.length;A++){let c=a[A];M.errorRequest(r,c,o)}}}),s.unref(),e[nt]=s,t[nt]=s,M.addListener(t,"error",function(r){this[ot]=r,this[yi][Hc](r)}),M.addListener(t,"end",function(){M.destroy(this,new br("other side closed",M.getSocketInfo(this)))}),M.addListener(t,"close",function(){let r=this[ot]||new br("closed",M.getSocketInfo(this));e[le]=null,this[nt]!=null&&this[nt].destroy(r),e[Jc]=e[Bt],Ct(e[Co]===0),e.emit("disconnect",e[fo],[e],r),e[es]()});let i=!1;return t.on("close",()=>{i=!0}),{version:"h2",defaultPipelining:1/0,write(...r){return D0(e,...r)},resume(){w0(e)},destroy(r,n){i?queueMicrotask(n):t.destroy(r).on("close",n)},get destroyed(){return t.destroyed},busy(){return!1}}}function w0(e){let t=e[le];t?.destroyed===!1&&(e[g0]===0&&e[Zd]===0?(t.unref(),e[nt].unref()):(t.ref(),e[nt].ref()))}function b0(e){this[le][ot]=e,this[yi][Hc](e)}function y0(e,t,s){if(s===0){let i=new mo(`HTTP/2: "frameError" received - type ${e}, code ${t}`);this[le][ot]=i,this[yi][Hc](i)}}function x0(){let e=new br("other side closed",M.getSocketInfo(this[le]));this.destroy(e),M.destroy(this[le],e)}function v0(e){let t=this[ot]||new br(`HTTP/2: "GOAWAY" frame received with code ${e}`,M.getSocketInfo(this)),s=this[yi];if(s[le]=null,s[h0]=null,this[nt]!=null&&(this[nt].destroy(t),this[nt]=null),M.destroy(this[le],t),s[Bt]<s[Ds].length){let i=s[Ds][s[Bt]];s[Ds][s[Bt]++]=null,M.errorRequest(s,i,t),s[Jc]=s[Bt]}Ct(s[Co]===0),s.emit("disconnect",s[fo],[s],t),s[es]()}function k0(e){return e!=="GET"&&e!=="HEAD"&&e!=="OPTIONS"&&e!=="TRACE"&&e!=="CONNECT"}function D0(e,t){let s=e[nt],{method:i,path:r,host:n,upgrade:o,expectContinue:a,signal:A,headers:c}=t,{body:l}=t;if(o)return M.errorRequest(e,t,new Error("Upgrade not supported for H2")),!1;let u={};for(let C=0;C<c.length;C+=2){let b=c[C+0],U=c[C+1];if(Array.isArray(U))for(let G=0;G<U.length;G++)u[b]?u[b]+=`,${U[G]}`:u[b]=U[G];else u[b]=U}let p,{hostname:h,port:d}=e[fo];u[d0]=n||`${h}${d?`:${d}`:""}`,u[E0]=i;let E=C=>{if(!t.aborted){if(t.completed){i==="CONNECT"&&p!=null&&M.destroy(p,C||new Yc);return}C=C||new Yc,M.errorRequest(e,t,C),p!=null&&M.destroy(p,C),M.destroy(l,C),e[Ds][e[Bt]++]=null,e[es]()}};try{t.onConnect(E)}catch(C){M.errorRequest(e,t,C)}if(t.aborted)return!1;if(i==="CONNECT"){s.ref(),p=s.request(u,{endStream:!1,signal:A});let C=!1,b=q=>{C=!0,p.off(_c,U),t.onUpgradeResponse(Number(q[Pc]),q,C0)},U=q=>{C=!0,p.off("response",b),t.onUpgradeError(q)},G=()=>{try{t.onUpgrade(null,null,p)}catch(q){p.off("response",b),E(q);return}t.aborted||(p.off("error",E),p.once(_c,U),e[Ds][e[Bt]++]=null)};return p.once("response",b),p.once("error",E),++s[$t],G(),p.once("close",()=>{!C&&t.completed&&(p.off("response",b),p.off(_c,U),t.onUpgradeError(new mo(`HTTP/2: "stream error" received - code ${p.rstCode}`))),s[$t]-=1,s[$t]===0&&s.unref()}),!0}u[m0]=r,u[f0]="https";let f=i==="PUT"||i==="POST"||i==="PATCH";l&&typeof l.read=="function"&&l.read(0);let g=M.bodyLength(l);if(M.isFormDataLike(l)){qd??=Ci().extractBody;let[C,b]=qd(l);u["content-type"]=b,l=C.stream,g=C.length}if(g==null&&(g=t.contentLength),(g===0||!f)&&(g=null),k0(i)&&g>0&&t.contentLength!=null&& Line truncated
`,this[Zk]=A??3e5,this[zk]=i??3e5,this[Kk]=f??!0,this[Xk]=B,this[bc]=Y,this[rs]=null,this[r0]=pe>-1?pe:-1,this[o0]=Wt??100,this[ee]=null,this[nt]=[],this[Lt]=0,this[at]=0,this[Qi]=jt=>xc(this,jt),this[i0]=jt=>md(this,jt)}get pipelining(){return this[cn]}set pipelining(t){this[cn]=t,this[Qi](!0)}get[Ii](){return this[nt].length-this[at]}get[Ci](){return this[at]-this[Lt]}get[Bi](){return this[nt].length-this[Lt]}get[Jk](){return!!this[ee]&&!this[fr]&&!this[ee].destroyed}get[wc](){return!!(this[ee]?.busy(null)||this[Bi]>=(Ed(this)||1)||this[Ii]>0)}[Ok](t){fd(this),this.once("connect",t)}[s0](t,s){let r=t.origin||this[It].origin,i=new Nk(r,t,s);return this[nt].push(i),this[xs]||(ys.bodyLength(i.body)==null&&ys.isIterable(i.body)?(this[xs]=1,queueMicrotask(()=>xc(this))):this[Qi](!0)),this[xs]&&this[is]!==2&&this[wc]&&(this[is]=2),this[is]<2}async[e0](){return new Promise(t=>{this[Bi]?this[rs]=t:t(null)})}async[t0](t){return new Promise(s=>{let r=this[nt].splice(this[at]);for(let o=0;o<r.length;o++){let n=r[o];ys.errorRequest(this,n,t)}let i=()=>{this[rs]&&(this[rs](),this[rs]=null),s(null)};this[ee]?(this[ee].destroy(t,i),this[ee]=null):queueMicrotask(i),this[Qi]()})}},A0=An();function md(e,t){if(e[Ci]===0&&t.code!=="UND_ERR_INFO"&&t.code!=="UND_ERR_SOCKET"){Mt(e[at]===e[Lt]);let s=e[nt].splice(e[Lt]);for(let r=0;r<s.length;r++){let i=s[r];ys.errorRequest(e,i,t)}Mt(e[Bi]===0)}}async function fd(e){Mt(!e[fr]),Mt(!e[ee]);let{host:t,hostname:s,protocol:r,port:i}=e[It];if(s[0]==="["){let o=s.indexOf("]");Mt(o!==-1);let n=s.substring(1,o);Mt(dd.isIP(n)),s=n}e[fr]=!0,mr.beforeConnect.hasSubscribers&&mr.beforeConnect.publish({connectParams:{host:t,hostname:s,protocol:r,port:i,version:e[ee]?.version,servername:e[ss],localAddress:e[fi]},connector:e[mi]});try{let o=await new Promise((n,a)=>{e[mi]({host:t,hostname:s,protocol:r,port:i,servername:e[ss],localAddress:e[fi]},(A,c)=>{A?a(A):n(c)})});if(e.destroyed){ys.destroy(o.on("error",gd),new Lk);return}Mt(o);try{e[ee]=o.alpnProtocol==="h2"?await a0(e,o):await n0(e,o)}catch(n){throw o.destroy().on("error",gd),n}e[fr]=!1,o[$k]=0,o[bc]=e[bc],o[Yk]=e,o[Hk]=null,mr.connected.hasSubscribers&&mr.connected.publish({connectParams:{host:t,hostname:s,protocol:r,port:i,version:e[ee]?.version,servername:e[ss],localAddress:e[fi]},connector:e[mi],socket:o}),e.emit("connect",e[It],[e])}catch(o){if(e.destroyed)return;if(e[fr]=!1,mr.connectError.hasSubscribers&&mr.connectError.publish({connectParams:{host:t,hostname:s,protocol:r,port:i,version:e[ee]?.version,servername:e[ss],localAddress:e[fi]},connector:e[mi],error:o}),o.code==="ERR_TLS_CERT_ALTNAME_INVALID")for(Mt(e[Ci]===0);e[Ii]>0&&e[nt][e[at]].servername===e[ss];){let n=e[nt][e[at]++];ys.errorRequest(e,n,o)}else md(e,o);e.emit("connectionError",e[It],[e],o)}e[Qi]()}function hd(e){e[is]=0,e.emit("drain",e[It],[e])}function xc(e,t){e[xs]!==2&&(e[xs]=2,c0(e,t),e[xs]=0,e[Lt]>256&&(e[nt].splice(0,e[Lt]),e[at]-=e[Lt],e[Lt]=0))}function c0(e,t){for(;;){if(e.destroyed){Mt(e[Ii]===0);return}if(e[rs]&&!e[Bi]){e[rs](),e[rs]=null;return}if(e[ee]&&e[ee].resume(),e[wc])e[is]=2;else if(e[is]===2){t?(e[is]=1,queueMicrotask(()=>hd(e))):hd(e);continue}if(e[Ii]===0||e[Ci]>=(Ed(e)||1))return;let s=e[nt][e[at]];if(e[It].protocol==="https:"&&e[ss]!==s.servername){if(e[Ci]>0)return;e[ss]=s.servername,e[ee]?.destroy(new Mk("servername changed"),()=>{e[ee]=null,xc(e)})}if(e[fr])return;if(!e[ee]){fd(e);return}if(e[ee].destroyed||e[ee].busy(s))return;!s.aborted&&e[ee].write(s)?e[at]++:e[nt].splice(e[at],1)}}Qd.exports=yc});var vc=Q((U2,Bd)=>{"use strict";var ln=class{constructor(){this.bottom=0,this.top=0,this.list=new Array(2048),this.next=null}isEmpty(){return this.top===this.bottom}isFull(){return(this.top+1&2047)===this.bottom}push(t){this.list[this.top]=t,this.top=this.top+1&2047}shift(){let t=this.list[this.bottom];return t===void 0?null:(this.list[this.bottom]=void 0,this.bottom=this.bottom+1&2047,t)}};Bd.exports=class{constructor(){this.head=this.tail=new ln}isEmpty(){return this.head.isEmpty()}push(t){this.head.isFull()&&(this.head=this.head.next=new ln),this.head.push(t)}shift(){let t=this.tail,s=t.shift();return t.isEmpty()&&t.next!==null&&(this.tail=t.next),s}}});var Id=Q((N2,Cd)=>{var{kFree:l0,kConnected:u0,kPending:p0,kQueued:g0,kRunning:h0,kSize:d0}=j(),vs=Symbol("pool"),kc=class{constructor(t){this[vs]=t}get connected(){return this[vs][u0]}get free(){return this[vs][l0]}get pending(){return this[vs][p0]}get queued(){return this[vs][g0]}get running(){return this[vs][h0]}get size(){return this[vs][d0]}};Cd.exports=kc});var Uc=Q((G2,Fd)=>{"use strict";var E0=rr(),m0=vc(),{kConnected:Dc,kSize:wd,kRunning:bd,kPending:yd,kQueued:wi,kBusy:f0,kFree:Q0,kUrl:B0,kClose:C0,kDestroy:I0,kDispatch:w0}=j(),b0=Id(),De=Symbol("clients"),Ce=Symbol("needDrain"),bi=Symbol("queue"),Rc=Symbol("closed resolve"),Tc=Symbol("onDrain"),xd=Symbol("onConnect"),vd=Symbol("onDisconnect"),kd=Symbol("onConnectionError"),Fc=Symbol("get dispatcher"),Rd=Symbol("add client"),Td=Sym Line truncated `,this[tD]=A??3e5,this[eD]=r??3e5,this[sD]=m??!0,this[iD]=C,this[jc]=U,this[ss]=null,this[AD]=q>-1?q:-1,this[lD]=ze??100,this[ie]=null,this[at]=[],this[Yt]=0,this[At]=0,this[kr]=Bs=>Zc(this,Bs),this[cD]=Bs=>uE(this,Bs)}get pipelining(){return this[yo]}set pipelining(t){this[yo]=t,this[kr](!0)}get[Tr](){return this[at].length-this[At]}get[Rr](){return this[At]-this[Yt]}get[Dr](){return this[at].length-this[Yt]}get[W0](){return!!this[ie]&&!this[vi]&&!this[ie].destroyed}get[Wc](){return!!(this[ie]?.busy(null)||this[Dr]>=(lE(this)||1)||this[Tr]>0)}[q0](t){pE(this),this.once("connect",t)}[aD](t,s){let i=t.origin||this[wt].origin,r=new Y0(i,t,s);return this[at].push(r),this[Ts]||(Rs.bodyLength(r.body)==null&&Rs.isIterable(r.body)?(this[Ts]=1,queueMicrotask(()=>Zc(this))):this[kr](!0)),this[Ts]&&this[is]!==2&&this[Wc]&&(this[is]=2),this[is]<2}async[nD](){return new Promise(t=>{this[Dr]?this[ss]=t:t(null)})}async[oD](t){return new Promise(s=>{let i=this[at].splice(this[At]);for(let n=0;n<i.length;n++){let o=i[n];Rs.errorRequest(this,o,t)}let r=()=>{this[ss]&&(this[ss](),this[ss]=null),s(null)};this[ie]?(this[ie].destroy(t,r),this[ie]=null):queueMicrotask(r),this[kr]()})}},gD=bo();function uE(e,t){if(e[Rr]===0&&t.code!=="UND_ERR_INFO"&&t.code!=="UND_ERR_SOCKET"){_t(e[At]===e[Yt]);let s=e[at].splice(e[Yt]);for(let i=0;i<s.length;i++){let r=s[i];Rs.errorRequest(e,r,t)}_t(e[Dr]===0)}}async function pE(e){_t(!e[vi]),_t(!e[ie]);let{host:t,hostname:s,protocol:i,port:r}=e[wt];if(s[0]==="["){let n=s.indexOf("]");_t(n!==-1);let o=s.substring(1,n);_t(cE.isIP(o)),s=o}e[vi]=!0,xi.beforeConnect.hasSubscribers&&xi.beforeConnect.publish({connectParams:{host:t,hostname:s,protocol:i,port:r,version:e[ie]?.version,servername:e[ts],localAddress:e[vr]},connector:e[xr]});try{let n=await new Promise((o,a)=>{e[xr]({host:t,hostname:s,protocol:i,port:r,servername:e[ts],localAddress:e[vr]},(A,c)=>{A?a(A):o(c)})});if(e.destroyed){Rs.destroy(n.on("error",aE),new J0);return}_t(n);try{e[ie]=n.alpnProtocol==="h2"?await pD(e,n):await uD(e,n)}catch(o){throw n.destroy().on("error",aE),o}e[vi]=!1,n[rD]=0,n[jc]=e[jc],n[V0]=e,n[z0]=null,xi.connected.hasSubscribers&&xi.connected.publish({connectParams:{host:t,hostname:s,protocol:i,port:r,version:e[ie]?.version,servername:e[ts],localAddress:e[vr]},connector:e[xr],socket:n}),e.emit("connect",e[wt],[e])}catch(n){if(e.destroyed)return;if(e[vi]=!1,xi.connectError.hasSubscribers&&xi.connectError.publish({connectParams:{host:t,hostname:s,protocol:i,port:r,version:e[ie]?.version,servername:e[ts],localAddress:e[vr]},connector:e[xr],error:n}),n.code==="ERR_TLS_CERT_ALTNAME_INVALID")for(_t(e[Rr]===0);e[Tr]>0&&e[at][e[At]].servername===e[ts];){let o=e[at][e[At]++];Rs.errorRequest(e,o,n)}else uE(e,n);e.emit("connectionError",e[wt],[e],n)}e[kr]()}function AE(e){e[is]=0,e.emit("drain",e[wt],[e])}function Zc(e,t){e[Ts]!==2&&(e[Ts]=2,hD(e,t),e[Ts]=0,e[Yt]>256&&(e[at].splice(0,e[Yt]),e[At]-=e[Yt],e[Yt]=0))}function hD(e,t){for(;;){if(e.destroyed){_t(e[Tr]===0);return}if(e[ss]&&!e[Dr]){e[ss](),e[ss]=null;return}if(e[ie]&&e[ie].resume(),e[Wc])e[is]=2;else if(e[is]===2){t?(e[is]=1,queueMicrotask(()=>AE(e))):AE(e);continue}if(e[Tr]===0||e[Rr]>=(lE(e)||1))return;let s=e[at][e[At]];if(e[wt].protocol==="https:"&&e[ts]!==s.servername){if(e[Rr]>0)return;e[ts]=s.servername,e[ie]?.destroy(new P0("servername changed"),()=>{e[ie]=null,Zc(e)})}if(e[vi])return;if(!e[ie]){pE(e);return}if(e[ie].destroyed||e[ie].busy(s))return;!s.aborted&&e[ie].write(s)?e[At]++:e[at].splice(e[At],1)}}gE.exports=zc});var Kc=Q((jY,hE)=>{"use strict";var xo=class{constructor(){this.bottom=0,this.top=0,this.list=new Array(2048),this.next=null}isEmpty(){return this.top===this.bottom}isFull(){return(this.top+1&2047)===this.bottom}push(t){this.list[this.top]=t,this.top=this.top+1&2047}shift(){let t=this.list[this.bottom];return t===void 0?null:(this.list[this.bottom]=void 0,this.bottom=this.bottom+1&2047,t)}};hE.exports=class{constructor(){this.head=this.tail=new xo}isEmpty(){return this.head.isEmpty()}push(t){this.head.isFull()&&(this.head=this.head.next=new xo),this.head.push(t)}shift(){let t=this.tail,s=t.shift();return t.isEmpty()&&t.next!==null&&(this.tail=t.next),s}}});var EE=Q((zY,dE)=>{var{kFree:dD,kConnected:ED,kPending:mD,kQueued:fD,kRunning:QD,kSize:BD}=Z(),Ss=Symbol("pool"),Xc=class{constructor(t){this[Ss]=t}get connected(){return this[Ss][ED]}get free(){return this[Ss][dD]}get pending(){return this[Ss][mD]}get queued(){return this[Ss][fD]}get running(){return this[Ss][QD]}get size(){return this[Ss][BD]}};dE.exports=Xc});var rl=Q((ZY,xE)=>{"use strict";var CD=pi(),ID=Kc(),{kConnected:$c,kSize:mE,kRunning:fE,kPending:QE,kQueued:Sr,kBusy:wD,kFree:bD,kUrl:yD,kClose:xD,kDestroy:vD,kDispatch:kD}=Z(),DD=EE(),Te=Symbol("clients"),ye=Symbol("needDrain"),Fr=Symbol("queue"),el=Symbol("closed resolve"),tl=Symbol("onDrain"),BE=Symbol("onConnect"),CE=Symbol("onDisconnect"),IE=Symbol("onConnectionError"),sl=Symbol("get dispatcher"),bE=Symbol("add client"),yE=Symbo Line truncated
${r.count} ${r.noun} ${r.is} pending: ${i.count} ${i.noun} ${i.is} pending:
${t.format(s)} ${t.format(s)}
`.trim())}};Lm.exports=Ul});var kn=Q((lY,Pm)=>{"use strict";var Ym=Symbol.for("undici.globalDispatcher.1"),{InvalidArgumentError:ZR}=L(),KR=Cr();Jm()===void 0&&Om(new KR);function Om(e){if(!e||typeof e.dispatch!="function")throw new ZR("Argument agent must implement Agent");Object.defineProperty(globalThis,Ym,{value:e,writable:!0,enumerable:!1,configurable:!1})}function Jm(){return globalThis[Ym]}Pm.exports={setGlobalDispatcher:Om,getGlobalDispatcher:Jm}});var Dn=Q((pY,Hm)=>{"use strict";Hm.exports=class{#e;constructor(t){if(typeof t!="object"||t===null)throw new TypeError("handler must be an object");this.#e=t}onConnect(...t){return this.#e.onConnect?.(...t)}onError(...t){return this.#e.onError?.(...t)}onUpgrade(...t){return this.#e.onUpgrade?.(...t)}onResponseStarted(...t){return this.#e.onResponseStarted?.(...t)}onHeaders(...t){return this.#e.onHeaders?.(...t)}onData(...t){return this.#e.onData?.(...t)}onComplete(...t){return this.#e.onComplete?.(...t)}onBodySent(...t){return this.#e.onBodySent?.(...t)}}});var qm=Q((gY,Vm)=>{"use strict";var XR=an();Vm.exports=e=>{let t=e?.maxRedirections;return s=>function(i,o){let{maxRedirections:n=t,...a}=i;if(!n)return s(i,o);let A=new XR(s,n,i,o);return s(a,A)}}});var jm=Q((hY,Wm)=>{"use strict";var $R=fn();Wm.exports=e=>t=>function(r,i){return t(r,new $R({...r,retryOptions:{...e,...r.retryOptions}},{handler:i,dispatch:t}))}});var Zm=Q((dY,zm)=>{"use strict";var eT=U(),{InvalidArgumentError:tT,RequestAbortedError:sT}=L(),rT=Dn(),Nl=class extends rT{#e=1024*1024;#t=null;#i=!1;#s=!1;#r=0;#A=null;#a=null;constructor({maxSize:t},s){if(super(s),t!=null&&(!Number.isFinite(t)||t<1))throw new tT("maxSize must be a number greater than 0");this.#e=t??this.#e,this.#a=s}onConnect(t){this.#t=t,this.#a.onConnect(this.#c.bind(this))}#c(t){this.#s=!0,this.#A=t}onHeaders(t,s,r,i){let n=eT.parseHeaders(s)["content-length"];if(n!=null&&n>this.#e)throw new sT(`Response size (${n}) larger than maxSize (${this.#e})`);return this.#s?!0:this.#a.onHeaders(t,s,r,i)}onError(t){this.#i||(t=this.#A??t,this.#a.onError(t))}onData(t){return this.#r=this.#r+t.length,this.#r>=this.#e&&(this.#i=!0,this.#s?this.#a.onError(this.#A):this.#a.onComplete([])),!0}onComplete(t){if(!this.#i){if(this.#s){this.#a.onError(this.reason);return}this.#a.onComplete(t)}}};function iT({maxSize:e}={maxSize:1024*1024}){return t=>function(r,i){let{dumpMaxSize:o=e}=r,n=new Nl({maxSize:o},i);return t(r,n)}}zm.exports=iT});var $m=Q((EY,Xm)=>{"use strict";var{isIP:oT}=require("node:net"),{lookup:nT}=require("node:dns"),aT=Dn(),{InvalidArgumentError:Tr,InformationalError:AT}=L(),Km=Math.pow(2,31)-1,Gl=class{#e=0;#t=0;#i=new Map;dualStack=!0;affinity=null;lookup=null;pick=null;constructor(t){this.#e=t.maxTTL,this.#t=t.maxItems,this.dualStack=t.dualStack,this.affinity=t.affinity,this.lookup=t.lookup??this.#s,this.pick=t.pick??this.#r}get full(){return this.#i.size===this.#t}runLookup(t,s,r){let i=this.#i.get(t.hostname);if(i==null&&this.full){r(null,t.origin);return}let o={affinity:this.affinity,dualStack:this.dualStack,lookup:this.lookup,pick:this.pick,...s.dns,maxTTL:this.#e,maxItems:this.#t};if(i==null)this.lookup(t,o,(n,a)=>{if(n||a==null||a.length===0){r(n??new AT("No DNS entries found"));return}this.setRecords(t,a);let A=this.#i.get(t.hostname),c=this.pick(t,A,o.affinity),u;typeof c.port=="number"?u=`:${c.port}`:t.port!==""?u=`:${t.port}`:u="",r(null,`${t.protocol}//${c.family===6?`[${c.address}]`:c.address}${u}`)});else{let n=this.pick(t,i,o.affinity);if(n==null){this.#i.delete(t.hostname),this.runLookup(t,s,r);return}let a;typeof n.port=="number"?a=`:${n.port}`:t.port!==""?a=`:${t.port}`:a="",r(null,`${t.protocol}//${n.family===6?`[${n.address}]`:n.address}${a}`)}}#s(t,s,r){nT(t.hostname,{all:!0,family:this.dualStack===!1?this.affinity:0,order:"ipv4first"},(i,o)=>{if(i)return r(i);let n=new Map;for(let a of o)n.set(`${a.address}:${a.family}`,a);r(null,n.values())})}#r(t,s,r){let i=null,{records:o,offset:n}=s,a;if(this.dualStack?(r==null&&(n==null||n===Km?(s.offset=0,r=4):(s.offset++,r=(s.offset&1)===1?6:4)),o[r]!=null&&o[r].ips.length>0?a=o[r]:a=o[r===4?6:4]):a=o[r],a==null||a.ips.length===0)return i;a.offset==null||a.offset===Km?a.offset=0:a.offset++;let A=a.offset%a.ips.length;return i=a.ips[A]??null,i==null?i:Date.now()-i.timestamp>i.ttl?(a.ips.splice(A,1),this.pick(t,s,r)):i}setRecords(t,s){let r=Date.now(),i={records:{4:null,6:null}};for(let o of s){o.timestamp=r,typeof o.ttl=="number"?o.ttl=Math.min(o.ttl,this.#e):o.ttl=this.#e;let n=i.records[o.family]??{ips:[]};n.ips.push(o),i.records[o.family]=n}this.#i.set(t.hostname,i)}getHandler(t,s){return new Ml(this,t,s)}},Ml=class extends aT{#e=null;#t=null;#i=null;#s=null;#r=null;constructor(t,{origin:s,handler:r,dispatch:i},o){super(r),this.#r=s,this.#s=r,this.#t={...o},this.#e=t,this.#i=i}onError(t){switch(t.code){case"ETIMEDOUT":case"ECONNREFUSED":{if(this.#e.dualStack){this.#e.runLookup(this.#r,this.#t,(s,r)=>{if(s)return this.#s.onError(s);let i={...this.#t,origin: Line truncated `.trim())}};Uf.exports=ru});var Pr=Q((bO,_f)=>{"use strict";var Mf=Symbol.for("undici.globalDispatcher.1"),{InvalidArgumentError:tS}=Y(),sS=Ri();Lf()===void 0&&Gf(new sS);function Gf(e){if(!e||typeof e.dispatch!="function")throw new tS("Argument agent must implement Agent");Object.defineProperty(globalThis,Mf,{value:e,writable:!0,enumerable:!1,configurable:!1})}function Lf(){return globalThis[Mf]}_f.exports={setGlobalDispatcher:Gf,getGlobalDispatcher:Lf}});var Ho=Q((xO,Yf)=>{"use strict";Yf.exports=class{#e;constructor(t){if(typeof t!="object"||t===null)throw new TypeError("handler must be an object");this.#e=t}onConnect(...t){return this.#e.onConnect?.(...t)}onError(...t){return this.#e.onError?.(...t)}onUpgrade(...t){return this.#e.onUpgrade?.(...t)}onResponseStarted(...t){return this.#e.onResponseStarted?.(...t)}onHeaders(...t){return this.#e.onHeaders?.(...t)}onData(...t){return this.#e.onData?.(...t)}onComplete(...t){return this.#e.onComplete?.(...t)}onBodySent(...t){return this.#e.onBodySent?.(...t)}}});var Pf=Q((vO,Of)=>{"use strict";var iS=wo();Of.exports=e=>{let t=e?.maxRedirections;return s=>function(r,n){let{maxRedirections:o=t,...a}=r;if(!o)return s(r,n);let A=new iS(s,o,r,n);return s(a,A)}}});var Hf=Q((kO,Jf)=>{"use strict";var rS=Uo();Jf.exports=e=>t=>function(i,r){return t(i,new rS({...i,retryOptions:{...e,...i.retryOptions}},{handler:r,dispatch:t}))}});var qf=Q((DO,Vf)=>{"use strict";var nS=N(),{InvalidArgumentError:oS,RequestAbortedError:aS}=Y(),AS=Ho(),nu=class extends AS{#e=1024*1024;#t=null;#s=!1;#r=!1;#i=0;#n=null;#a=null;constructor({maxSize:t},s){if(super(s),t!=null&&(!Number.isFinite(t)||t<1))throw new oS("maxSize must be a number greater than 0");this.#e=t??this.#e,this.#a=s}onConnect(t){this.#t=t,this.#a.onConnect(this.#c.bind(this))}#c(t){this.#r=!0,this.#n=t}onHeaders(t,s,i,r){let o=nS.parseHeaders(s)["content-length"];if(o!=null&&o>this.#e)throw new aS(`Response size (${o}) larger than maxSize (${this.#e})`);return this.#r?!0:this.#a.onHeaders(t,s,i,r)}onError(t){this.#s||(t=this.#n??t,this.#a.onError(t))}onData(t){return this.#i=this.#i+t.length,this.#i>=this.#e&&(this.#s=!0,this.#r?this.#a.onError(this.#n):this.#a.onComplete([])),!0}onComplete(t){if(!this.#s){if(this.#r){this.#a.onError(this.reason);return}this.#a.onComplete(t)}}};function cS({maxSize:e}={maxSize:1024*1024}){return t=>function(i,r){let{dumpMaxSize:n=e}=i,o=new nu({maxSize:n},r);return t(i,o)}}Vf.exports=cS});var zf=Q((RO,jf)=>{"use strict";var{isIP:lS}=require("node:net"),{lookup:uS}=require("node:dns"),pS=Ho(),{InvalidArgumentError:_i,InformationalError:gS}=Y(),Wf=Math.pow(2,31)-1,ou=class{#e=0;#t=0;#s=new Map;dualStack=!0;affinity=null;lookup=null;pick=null;constructor(t){this.#e=t.maxTTL,this.#t=t.maxItems,this.dualStack=t.dualStack,this.affinity=t.affinity,this.lookup=t.lookup??this.#r,this.pick=t.pick??this.#i}get full(){return this.#s.size===this.#t}runLookup(t,s,i){let r=this.#s.get(t.hostname);if(r==null&&this.full){i(null,t.origin);return}let n={affinity:this.affinity,dualStack:this.dualStack,lookup:this.lookup,pick:this.pick,...s.dns,maxTTL:this.#e,maxItems:this.#t};if(r==null)this.lookup(t,n,(o,a)=>{if(o||a==null||a.length===0){i(o??new gS("No DNS entries found"));return}this.setRecords(t,a);let A=this.#s.get(t.hostname),c=this.pick(t,A,n.affinity),l;typeof c.port=="number"?l=`:${c.port}`:t.port!==""?l=`:${t.port}`:l="",i(null,`${t.protocol}//${c.family===6?`[${c.address}]`:c.address}${l}`)});else{let o=this.pick(t,r,n.affinity);if(o==null){this.#s.delete(t.hostname),this.runLookup(t,s,i);return}let a;typeof o.port=="number"?a=`:${o.port}`:t.port!==""?a=`:${t.port}`:a="",i(null,`${t.protocol}//${o.family===6?`[${o.address}]`:o.address}${a}`)}}#r(t,s,i){uS(t.hostname,{all:!0,family:this.dualStack===!1?this.affinity:0,order:"ipv4first"},(r,n)=>{if(r)return i(r);let o=new Map;for(let a of n)o.set(`${a.address}:${a.family}`,a);i(null,o.values())})}#i(t,s,i){let r=null,{records:n,offset:o}=s,a;if(this.dualStack?(i==null&&(o==null||o===Wf?(s.offset=0,i=4):(s.offset++,i=(s.offset&1)===1?6:4)),n[i]!=null&&n[i].ips.length>0?a=n[i]:a=n[i===4?6:4]):a=n[i],a==null||a.ips.length===0)return r;a.offset==null||a.offset===Wf?a.offset=0:a.offset++;let A=a.offset%a.ips.length;return r=a.ips[A]??null,r==null?r:Date.now()-r.timestamp>r.ttl?(a.ips.splice(A,1),this.pick(t,s,i)):r}setRecords(t,s){let i=Date.now(),r={records:{4:null,6:null}};for(let n of s){n.timestamp=i,typeof n.ttl=="number"?n.ttl=Math.min(n.ttl,this.#e):n.ttl=this.#e;let o=r.records[n.family]??{ips:[]};o.ips.push(n),r.records[n.family]=o}this.#s.set(t.hostname,r)}getHandler(t,s){return new au(this,t,s)}},au=class extends pS{#e=null;#t=null;#s=null;#r=null;#i=null;constructor(t,{origin:s,handler:i,dispatch:r},n){super(i),this.#i=s,this.#r=i,this.#t={...n},this.#e=t,this.#s=r}onError(t){switch(t.code){case"ETIMEDOUT":case"ECONNREFUSED":{if(this.#e.dualStack){this.#e.runLookup(this.#i,this.#t,(s,i)=>{if(s)return this.#r.onError(s);let r={...this.#t,origin: Line truncated
${o}`;break;case"retry":SB(o)&&(s[i]=o);break;case"id":UB(o)&&(s[i]=o);break;case"event":o.length>0&&(s[i]=o);break}}processEvent(t){t.retry&&SB(t.retry)&&(this.state.reconnectionTime=parseInt(t.retry,10)),t.id&&UB(t.id)&&(this.state.lastEventId=t.id),t.data!==void 0&&this.push({type:t.event||"message",options:{data:t.data,lastEventId:this.state.lastEventId,origin:this.state.origin}})}clearEvent(){this.event={data:void 0,event:void 0,id:void 0,retry:void 0}}};NB.exports={EventSourceStream:Iu}});var HB=Q((WY,PB)=>{"use strict";var{pipeline:fN}=require("node:stream"),{fetching:QN}=Mi(),{makeRequest:BN}=Ur(),{webidl:Pt}=ge(),{EventSourceStream:CN}=GB(),{parseMIMEType:IN}=ve(),{createFastMessageEvent:wN}=Yr(),{isNetworkError:MB}=Ni(),{delay:bN}=Bu(),{kEnumerableProperty:Js}=U(),{environmentSettingsObject:LB}=Ue(),_B=!1,YB=3e3,Zi=0,OB=1,Ki=2,yN="anonymous",xN="use-credentials",Vr=class e extends EventTarget{#e={open:null,error:null,message:null};#t=null;#i=!1;#s=Zi;#r=null;#A=null;#a;#c;constructor(t,s={}){super(),Pt.util.markAsUncloneable(this);let r="EventSource constructor";Pt.argumentLengthCheck(arguments,1,r),_B||(_B=!0,process.emitWarning("EventSource is experimental, expect them to change at any time.",{code:"UNDICI-ES"})),t=Pt.converters.USVString(t,r,"url"),s=Pt.converters.EventSourceInitDict(s,r,"eventSourceInitDict"),this.#a=s.dispatcher,this.#c={lastEventId:"",reconnectionTime:YB};let i=LB,o;try{o=new URL(t,i.settingsObject.baseUrl),this.#c.origin=o.origin}catch(A){throw new DOMException(A,"SyntaxError")}this.#t=o.href;let n=yN;s.withCredentials&&(n=xN,this.#i=!0);let a={redirect:"follow",keepalive:!0,mode:"cors",credentials:n==="anonymous"?"same-origin":"omit",referrer:"no-referrer"};a.client=LB.settingsObject,a.headersList=[["accept",{name:"accept",value:"text/event-stream"}]],a.cache="no-store",a.initiator="other",a.urlList=[new URL(this.#t)],this.#r=BN(a),this.#l()}get readyState(){return this.#s}get url(){return this.#t}get withCredentials(){return this.#i}#l(){if(this.#s===Ki)return;this.#s=Zi;let t={request:this.#r,dispatcher:this.#a},s=r=>{MB(r)&&(this.dispatchEvent(new Event("error")),this.close()),this.#g()};t.processResponseEndOfBody=s,t.processResponse=r=>{if(MB(r))if(r.aborted){this.close(),this.dispatchEvent(new Event("error"));return}else{this.#g();return}let i=r.headersList.get("content-type",!0),o=i!==null?IN(i):"failure",n=o!=="failure"&&o.essence==="text/event-stream";if(r.status!==200||n===!1){this.close(),this.dispatchEvent(new Event("error"));return}this.#s=OB,this.dispatchEvent(new Event("open")),this.#c.origin=r.urlList[r.urlList.length-1].origin;let a=new CN({eventSourceSettings:this.#c,push:A=>{this.dispatchEvent(wN(A.type,A.options))}});fN(r.body.stream,a,A=>{A?.aborted===!1&&(this.close(),this.dispatchEvent(new Event("error")))})},this.#A=QN(t)}async#g(){this.#s!==Ki&&(this.#s=Zi,this.dispatchEvent(new Event("error")),await bN(this.#c.reconnectionTime),this.#s===Zi&&(this.#c.lastEventId.length&&this.#r.headersList.set("last-event-id",this.#c.lastEventId,!0),this.#l()))}close(){Pt.brandCheck(this,e),this.#s!==Ki&&(this.#s=Ki,this.#A.abort(),this.#r=null)}get onopen(){return this.#e.open}set onopen(t){this.#e.open&&this.removeEventListener("open",this.#e.open),typeof t=="function"?(this.#e.open=t,this.addEventListener("open",t)):this.#e.open=null}get onmessage(){return this.#e.message}set onmessage(t){this.#e.message&&this.removeEventListener("message",this.#e.message),typeof t=="function"?(this.#e.message=t,this.addEventListener("message",t)):this.#e.message=null}get onerror(){return this.#e.error}set onerror(t){this.#e.error&&this.removeEventListener("error",this.#e.error),typeof t=="function"?(this.#e.error=t,this.addEventListener("error",t)):this.#e.error=null}},JB={CONNECTING:{__proto__:null,configurable:!1,enumerable:!0,value:Zi,writable:!1},OPEN:{__proto__:null,configurable:!1,enumerable:!0,value:OB,writable:!1},CLOSED:{__proto__:null,configurable:!1,enumerable:!0,value:Ki,writable:!1}};Object.defineProperties(Vr,JB);Object.defineProperties(Vr.prototype,JB);Object.defineProperties(Vr.prototype,{close:Js,onerror:Js,onmessage:Js,onopen:Js,readyState:Js,url:Js,withCredentials:Js});Pt.converters.EventSourceInitDict=Pt.dictionaryConverter([{key:"withCredentials",converter:Pt.converters.boolean,defaultValue:()=>!1},{key:"dispatcher",converter:Pt.converters.any}]);PB.exports={EventSource:Vr,defaultReconnectionTime:YB}});var ma=Q((jY,R)=>{"use strict";var vN=Qr(),VB=ti(),kN=Br(),DN=Od(),RN=Cr(),TN=Wc(),FN=uE(),SN=mE(),qB=L(),Ea=U(),{InvalidArgumentError:da}=qB,qr=rm(),UN=ri(),NN=kl(),GN=_m(),MN=Tl(),LN=dl(),_N=fn(),{getGlobalDispatcher:WB,setGlobalDispatcher:YN}=kn(),ON=Dn(),JN=an(),PN=An();Object.assign(VB.prototype,qr);R.exports.Dispatcher=VB;R.exports.Client=vN;R.exports.Pool=kN;R.exports.BalancedPool=DN;R.exports.Agent=RN;R.exports.ProxyAgent=TN;R.exports.EnvHttpProxyAgent=FN;R.exports.RetryAgent=SN;R.exports.RetryHandler=_N;R.exports.DecoratorHandler=ON;R.exports.Redire Line truncated ${A}`,this.dataLineCount++;return}if(Ra(t,r,kM)){SM(t,n)&&(this.setEventFieldSize("retry",t.length-n),s.retry=t.toString("utf8",n));return}if(Ra(t,r,vM)){FM(t,n)&&(this.setEventFieldSize("id",t.length-n),s.id=t.toString("utf8",n));return}Ra(t,r,xM)&&n<t.length&&(this.setEventFieldSize("event",t.length-n),s.event=t.toString("utf8",n))}setEventFieldSize(t,s){let i=this.eventSize-this.eventFieldSizes[t]+s;if(this.maxEventSize>0&&i>this.maxEventSize)throw Ta();this.eventSize=i,this.eventFieldSizes[t]=s}processEvent(t){t.retry&&BM(t.retry)&&(this.state.reconnectionTime=parseInt(t.retry,10)),t.id&&CM(t.id)&&(this.state.lastEventId=t.id),t.data!==void 0&&this.push({type:t.event||"message",options:{data:t.data,lastEventId:this.state.lastEventId,origin:this.state.origin}})}clearEvent(){this.event.data=void 0,this.event.event=void 0,this.event.id=void 0,this.event.retry=void 0,this.eventSize=0,this.dataLineCount=0,this.eventFieldSizes.data=0,this.eventFieldSizes.event=0,this.eventFieldSizes.id=0,this.eventFieldSizes.retry=0}hasPendingEvent(){return this.event.data!==void 0||this.event.event!==void 0||this.event.id!==void 0||this.event.retry!==void 0}hasCurrentByte(){return this.chunkIndex<this.chunks.length&&this.pos<this.chunks[this.chunkIndex].length}currentByte(){return this.chunks[this.chunkIndex][this.pos]}consumeCurrentByte(){this.advanceCursor(),this.syncLineStartToCursor()}advanceCursor(){for(this.pos++;this.chunkIndex<this.chunks.length&&this.pos>=this.chunks[this.chunkIndex].length;)this.chunkIndex++,this.pos=0}syncLineStartToCursor(){this.lineChunkIndex=this.chunkIndex,this.linePos=this.pos,this.lineLength=0,this.dropConsumedChunks()}dropConsumedChunks(){if(this.lineChunkIndex>0){let t=this.lineChunkIndex;this.chunks.splice(0,t),this.lineChunkIndex=0,this.chunkIndex-=t}this.chunkIndex===this.chunks.length&&(this.chunks.length=0,this.chunkIndex=0,this.pos=0,this.lineChunkIndex=0,this.linePos=0)}readLine(){if(this.lineChunkIndex===this.chunkIndex)return this.chunks[this.chunkIndex].subarray(this.linePos,this.pos);let t=[],s=0;for(let i=this.lineChunkIndex;i<=this.chunkIndex;i++){let r=this.chunks[i],n=i===this.lineChunkIndex?this.linePos:0,o=i===this.chunkIndex?this.pos:r.length,a=r.subarray(n,o);s+=a.length,t.push(a)}return Buffer.concat(t,s)}peekBufferedByte(t){let s=this.lineChunkIndex,i=this.linePos;for(;s<this.chunks.length;){let r=this.chunks[s],n=r.length-i;if(t<n)return r[i+t];t-=n,s++,i=0}}discardLeadingBytes(t){for(;t>0&&this.lineChunkIndex<this.chunks.length;){let i=this.chunks[this.lineChunkIndex].length-this.linePos;t<i?(this.linePos+=t,t=0):(t-=i,this.lineChunkIndex++,this.linePos=0)}this.chunkIndex=this.lineChunkIndex,this.pos=this.linePos,this.dropConsumedChunks()}handleBOM(){let t=this.peekBufferedByte(0),s=this.peekBufferedByte(1),i=this.peekBufferedByte(2);return s===void 0?(t===$i[0]||(this.checkBOM=!1),!0):i===void 0?t===$i[0]&&s===$i[1]?!0:(this.checkBOM=!1,!1):(t===$i[0]&&s===$i[1]&&i===$i[2]&&this.discardLeadingBytes(3),this.checkBOM=!1,!this.hasCurrentByte())}};xC.exports={EventSourceStream:Zu}});var NC=Q((a1,UC)=>{"use strict";var{pipeline:UM}=require("node:stream"),{fetching:NM}=Wr(),{makeRequest:MM}=Pi(),{webidl:Ht}=Ee(),{EventSourceStream:GM}=vC(),{parseMIMEType:LM}=De(),{createFastMessageEvent:_M}=ji(),{isNetworkError:kC}=Vr(),{delay:YM}=zu(),{kEnumerableProperty:js}=N(),{environmentSettingsObject:DC}=Ge(),{getGlobalDispatcher:OM}=Pr(),RC=!1,TC=3e3,cn=0,SC=1,ln=2,PM="anonymous",JM="use-credentials",er=class e extends EventTarget{#e={open:null,error:null,message:null};#t=null;#s=!1;#r=cn;#i=null;#n=null;#a;#c;constructor(t,s={}){super(),Ht.util.markAsUncloneable(this);let i="EventSource constructor";Ht.argumentLengthCheck(arguments,1,i),RC||(RC=!0,process.emitWarning("EventSource is experimental, expect them to change at any time.",{code:"UNDICI-ES"})),t=Ht.converters.USVString(t,i,"url"),s=Ht.converters.EventSourceInitDict(s,i,"eventSourceInitDict"),this.#a=s.dispatcher,this.#c={lastEventId:"",reconnectionTime:TC};let r=DC,n;try{n=new URL(t,r.settingsObject.baseUrl),this.#c.origin=n.origin}catch(A){throw new DOMException(A,"SyntaxError")}this.#t=n.href;let o=PM;s.withCredentials&&(o=JM,this.#s=!0);let a={redirect:"follow",keepalive:!0,mode:"cors",credentials:o==="anonymous"?"same-origin":"omit",referrer:"no-referrer"};a.client=DC.settingsObject,a.headersList=[["accept",{name:"accept",value:"text/event-stream"}]],a.cache="no-store",a.initiator="other",a.urlList=[new URL(this.#t)],this.#i=MM(a),this.#l()}get readyState(){return this.#r}get url(){return this.#t}get withCredentials(){return this.#s}#l(){if(this.#r===ln)return;this.#r=cn;let t={request:this.#i,dispatcher:this.#a},s=i=>{kC(i)&&(this.dispatchEvent(new Event("error")),this.close()),this.#p()};t.processResponseEndOfBody=s,t.processResponse=i=>{if(kC(i))if(i.aborted){this.close(),this.dispatchEvent(new Event("error"));return}else{this.#p();return}let r=i.headersList.get("content-type",!0),n=r!==null?LM(r):"failure",o=n!= Line truncated
`+e.errors.map(t=>` - ${t.message}`).join(` `+e.errors.map(t=>` - ${t.message}`).join(`
`)}var QM=class extends Error{constructor(e,t,s){super(fM(s)),this.request=e,this.headers=t,this.response=s,this.errors=s.errors,this.data=s.data,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}name="GraphqlResponseError";errors;data},BM=["method","baseUrl","url","headers","request","query","mediaType","operationName"],CM=["query","method","url"],LC=/\/api\/v3\/?$/;function IM(e,t,s){if(s){if(typeof t=="string"&&"query"in s)return Promise.reject(new Error('[@octokit/graphql] "query" cannot be used as variable name'));for(let n in s)if(CM.includes(n))return Promise.reject(new Error(`[@octokit/graphql] "${n}" cannot be used as variable name`))}let r=typeof t=="string"?Object.assign({query:t},s):t,i=Object.keys(r).reduce((n,a)=>BM.includes(a)?(n[a]=r[a],n):(n.variables||(n.variables={}),n.variables[a]=r[a],n),{}),o=r.baseUrl||e.endpoint.DEFAULTS.baseUrl;return LC.test(o)&&(i.url=o.replace(LC,"/api/graphql")),e(i).then(n=>{if(n.data.errors){let a={};for(let A of Object.keys(n.headers))a[A]=n.headers[A];throw new QM(i,a,n.data)}return n.data.data})}function Gu(e,t){let s=e.defaults(t);return Object.assign((i,o)=>IM(s,i,o),{defaults:Gu.bind(null,s),endpoint:s.endpoint})}var aJ=Gu(io,{headers:{"user-agent":`octokit-graphql.js/${mM} ${ds()}`},method:"POST",url:"/graphql"});function _C(e){return Gu(e,{method:"POST",url:"/graphql"})}var Mu="(?:[a-zA-Z0-9_-]+)",YC="\\.",OC=new RegExp(`^${Mu}${YC}${Mu}${YC}${Mu}$`),wM=OC.test.bind(OC);async function bM(e){let t=wM(e),s=e.startsWith("v1.")||e.startsWith("ghs_"),r=e.startsWith("ghu_");return{type:"token",token:e,tokenType:t?"app":s?"installation":r?"user-to-server":"oauth"}}function yM(e){return e.split(/\./).length===3?`bearer ${e}`:`token ${e}`}async function xM(e,t,s,r){let i=t.endpoint.merge(s,r);return i.headers.authorization=yM(e),t(i)}var JC=function(t){if(!t)throw new Error("[@octokit/auth-token] No token passed to createTokenAuth");if(typeof t!="string")throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string");return t=t.replace(/^(token|bearer) +/i,""),Object.assign(bM.bind(null,t),{hook:xM.bind(null,t)})};var Lu="7.0.6";var PC=()=>{},vM=console.warn.bind(console),kM=console.error.bind(console);function DM(e={}){return typeof e.debug!="function"&&(e.debug=PC),typeof e.info!="function"&&(e.info=PC),typeof e.warn!="function"&&(e.warn=vM),typeof e.error!="function"&&(e.error=kM),e}var HC=`octokit-core.js/${Lu} ${ds()}`,Ta=class{static VERSION=Lu;static defaults(t){return class extends this{constructor(...r){let i=r[0]||{};if(typeof t=="function"){super(t(i));return}super(Object.assign({},t,i,i.userAgent&&t.userAgent?{userAgent:`${i.userAgent} ${t.userAgent}`}:null))}}}static plugins=[];static plugin(...t){let s=this.plugins;return class extends this{static plugins=s.concat(t.filter(i=>!s.includes(i)))}}constructor(t={}){let s=new mC.Collection,r={baseUrl:io.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},t.request,{hook:s.bind(null,"request")}),mediaType:{previews:[],format:""}};if(r.headers["user-agent"]=t.userAgent?`${t.userAgent} ${HC}`:HC,t.baseUrl&&(r.baseUrl=t.baseUrl),t.previews&&(r.mediaType.previews=t.previews),t.timeZone&&(r.headers["time-zone"]=t.timeZone),this.request=io.defaults(r),this.graphql=_C(this.request).defaults(r),this.log=DM(t.log),this.hook=s,t.authStrategy){let{authStrategy:o,...n}=t,a=o(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:n},t.auth));s.wrap("request",a.hook),this.auth=a}else if(!t.auth)this.auth=async()=>({type:"unauthenticated"});else{let o=JC(t.auth);s.wrap("request",o.hook),this.auth=o}let i=this.constructor;for(let o=0;o<i.plugins.length;++o)Object.assign(this,i.plugins[o](this,t))}request;graphql;log;hook;auth};var _u="17.0.0";var RM={actions:{addCustomLabelsToSelfHostedRunnerForOrg:["POST /orgs/{org}/actions/runners/{runner_id}/labels"],addCustomLabelsToSelfHostedRunnerForRepo:["POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],addRepoAccessToSelfHostedRunnerGroupInOrg:["PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id}"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],approveWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve"],cancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel"],createEnvironmentVariable:["POST /repos/{owner}/{repo}/environments/{environment_name}/variables"],createHostedRunnerForOrg:["POST /orgs/{org}/actions/hosted-runners"],createOrUpdateEnvironmentSecret:["PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}"],createOrgVariable:["POST /orgs/{org}/actions/variables"],cre Line truncated `)}var ML=class extends Error{constructor(e,t,s){super(NL(s)),this.request=e,this.headers=t,this.response=s,this.errors=s.errors,this.data=s.data,Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}name="GraphqlResponseError";errors;data},GL=["method","baseUrl","url","headers","request","query","mediaType","operationName"],LL=["query","method","url"],DI=/\/api\/v3\/?$/;function _L(e,t,s){if(s){if(typeof t=="string"&&"query"in s)return Promise.reject(new Error('[@octokit/graphql] "query" cannot be used as variable name'));for(let o in s)if(LL.includes(o))return Promise.reject(new Error(`[@octokit/graphql] "${o}" cannot be used as variable name`))}let i=typeof t=="string"?Object.assign({query:t},s):t,r=Object.keys(i).reduce((o,a)=>GL.includes(a)?(o[a]=i[a],o):(o.variables||(o.variables={}),o.variables[a]=i[a],o),{}),n=i.baseUrl||e.endpoint.DEFAULTS.baseUrl;return DI.test(n)&&(r.url=n.replace(DI,"/api/graphql")),e(r).then(o=>{if(o.data.errors){let a={};for(let A of Object.keys(o.headers))a[A]=o.headers[A];throw new ML(r,a,o.data)}return o.data.data})}function up(e,t){let s=e.defaults(t);return Object.assign((r,n)=>_L(s,r,n),{defaults:up.bind(null,s),endpoint:s.endpoint})}var CP=up(mn,{headers:{"user-agent":`octokit-graphql.js/${UL} ${hs()}`},method:"POST",url:"/graphql"});function RI(e){return up(e,{method:"POST",url:"/graphql"})}var pp="(?:[a-zA-Z0-9_-]+)",TI="\\.",SI=new RegExp(`^${pp}${TI}${pp}${TI}${pp}$`),YL=SI.test.bind(SI);async function OL(e){let t=YL(e),s=e.startsWith("v1.")||e.startsWith("ghs_"),i=e.startsWith("ghu_");return{type:"token",token:e,tokenType:t?"app":s?"installation":i?"user-to-server":"oauth"}}function PL(e){return e.split(/\./).length===3?`bearer ${e}`:`token ${e}`}async function JL(e,t,s,i){let r=t.endpoint.merge(s,i);return r.headers.authorization=PL(e),t(r)}var FI=function(t){if(!t)throw new Error("[@octokit/auth-token] No token passed to createTokenAuth");if(typeof t!="string")throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string");return t=t.replace(/^(token|bearer) +/i,""),Object.assign(OL.bind(null,t),{hook:JL.bind(null,t)})};var gp="7.0.6";var UI=()=>{},HL=console.warn.bind(console),VL=console.error.bind(console);function qL(e={}){return typeof e.debug!="function"&&(e.debug=UI),typeof e.info!="function"&&(e.info=UI),typeof e.warn!="function"&&(e.warn=HL),typeof e.error!="function"&&(e.error=VL),e}var NI=`octokit-core.js/${gp} ${hs()}`,ja=class{static VERSION=gp;static defaults(t){return class extends this{constructor(...i){let r=i[0]||{};if(typeof t=="function"){super(t(r));return}super(Object.assign({},t,r,r.userAgent&&t.userAgent?{userAgent:`${r.userAgent} ${t.userAgent}`}:null))}}}static plugins=[];static plugin(...t){let s=this.plugins;return class extends this{static plugins=s.concat(t.filter(r=>!s.includes(r)))}}constructor(t={}){let s=new AI.Collection,i={baseUrl:mn.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},t.request,{hook:s.bind(null,"request")}),mediaType:{previews:[],format:""}};if(i.headers["user-agent"]=t.userAgent?`${t.userAgent} ${NI}`:NI,t.baseUrl&&(i.baseUrl=t.baseUrl),t.previews&&(i.mediaType.previews=t.previews),t.timeZone&&(i.headers["time-zone"]=t.timeZone),this.request=mn.defaults(i),this.graphql=RI(this.request).defaults(i),this.log=qL(t.log),this.hook=s,t.authStrategy){let{authStrategy:n,...o}=t,a=n(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:o},t.auth));s.wrap("request",a.hook),this.auth=a}else if(!t.auth)this.auth=async()=>({type:"unauthenticated"});else{let n=FI(t.auth);s.wrap("request",n.hook),this.auth=n}let r=this.constructor;for(let n=0;n<r.plugins.length;++n)Object.assign(this,r.plugins[n](this,t))}request;graphql;log;hook;auth};var hp="17.0.0";var WL={actions:{addCustomLabelsToSelfHostedRunnerForOrg:["POST /orgs/{org}/actions/runners/{runner_id}/labels"],addCustomLabelsToSelfHostedRunnerForRepo:["POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels"],addRepoAccessToSelfHostedRunnerGroupInOrg:["PUT /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories/{repository_id}"],addSelectedRepoToOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}"],addSelectedRepoToOrgVariable:["PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id}"],approveWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve"],cancelWorkflowRun:["POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel"],createEnvironmentVariable:["POST /repos/{owner}/{repo}/environments/{environment_name}/variables"],createHostedRunnerForOrg:["POST /orgs/{org}/actions/hosted-runners"],createOrUpdateEnvironmentSecret:["PUT /repos/{owner}/{repo}/environments/{environment_name}/secrets/{secret_name}"],createOrUpdateOrgSecret:["PUT /orgs/{org}/actions/secrets/{secret_name}"],createOrUpdateRepoSecret:["PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}"],createOrgVariable:["POST /orgs/{org}/actions/variables"],cre Line truncated
globstar while`,e,l,t,p,g),this.matchOne(e.slice(l),t.slice(p),s))return this.debug("globstar found match!",l,a,g),!0;if(g==="."||g===".."||!r.dot&&g.charAt(0)==="."){this.debug("dot detected!",e,l,t,p);break}this.debug("globstar swallow a segment, and continue"),l++}return!!(s&&(this.debug(` `)},Y_=class{gen;threads=[];match=!1;runs=[];ids=[];constructor(e){this.gen=e}get dead(){return!this.threads.length&&!this.match&&!this.runs.length}},Uw=(e,t)=>e.ids.length!==t.ids.length||e.runs.length!==t.runs.length||e.ids.some((s,i)=>s!==t.ids[i])?!1:e.runs.every(s=>t.runs.some(i=>i.neg===s.neg&&Uw(i.state,s.state))),tw=e=>({has:new Float64Array(e),seen:new Float64Array(e),gen:0}),Lp=class{dots;dotStart;#e;constructor(e,t,s){this.dots=e,this.dotStart=t,this.#e=s}state(){return new Y_(++this.#e.gen)}done(e){return e.ids=e.threads.map(t=>t.id).sort((t,s)=>t-s),e.match&&e.ids.unshift(Mp.id),e}#t(e,t){let{seen:s}=this.#e;return s[t.id]===e.gen?!1:(s[t.id]=e.gen,!0)}#s(e,t){let{has:s}=this.#e;return s[t.id]===e.gen?!1:(s[t.id]=e.gen,e.threads.push(t),!0)}restricted(e,t){return t&&(this.dots||this.dotStart&&e===0)}add(e,t,s,i){switch(t.t){case"split":if(!this.#t(e,t))return;for(let n of t.out)this.add(e,n,s,i);return;case"neg":if(!this.#t(e,t))return;let r=this.state();return this.add(r,t.sub,s,!1),this.done(r),this.addRun(e,{t:"run",neg:t,state:r},s,i);case"star":if(this.restricted(s,i))return;this.#s(e,t)&&this.add(e,t.next,s,i);return;case"match":e.match=!0;return;default:this.#s(e,t)}}addRun(e,t,s,i){for(let r of e.runs)if(r.neg===t.neg&&Uw(r.state,t.state))return;e.runs.push(t),t.state.match||this.add(e,t.neg.next,s,i)}step(e,t,s,i){let r=this.state();for(let n of e.threads)switch(n.t){case"lit":Jp(n,t)&&this.add(r,n.next,s+1,i);break;case"any":this.restricted(s,i)||this.add(r,n.next,s+1,i);break;case"class":!this.restricted(s,i)&&n.re.test(t)&&this.add(r,n.next,s+1,i);break;case"star":this.restricted(s,i)||this.add(r,n,s+1,i)}if(!this.restricted(s,i))for(let n of e.runs){if(n.state.dead){this.addRun(r,n,s+1,i);continue}let o=this.step(n.state,t,s,!1);this.addRun(r,{t:"run",neg:n.neg,state:o},s+1,i)}return this.done(r)}},Nw=e=>{let t=e.ids.join();if(!e.runs.length)return t;let s=e.runs.map(i=>`${i.neg.id}(${Nw(i.state)})`);return`${t}|${s.sort().join("|")}`},O_=1<<18,P_=class{#e;#t=new Map;#s=0;#r;#i;#n;#a;constructor(e,t){this.#e=e,this.#n=new Lp(!1,!1,t),this.#a=new Lp(!1,!0,t)}#c(e){return{state:e,match:e.match,dead:e.dead,next:new Map}}#l(e){let t=Nw(e),s=this.#t.get(t);if(s)return s;this.#s>O_&&this.#p();let i=this.#c(e);return this.#t.set(t,i),this.#s+=t.length+1,i}#p(){this.#t.clear(),this.#s=0,this.#r=void 0,this.#i=void 0}start(e){if(e){if(this.#i)return this.#i;let s=this.#a.state();return this.#a.add(s,this.#e,0,!0),this.#i=this.#c(this.#a.done(s))}if(this.#r)return this.#r;let t=this.#n.state();return this.#n.add(t,this.#e,0,!0),this.#r=this.#l(this.#n.done(t))}next(e,t){let s=t.charCodeAt(0),i=s<128?void 0:e.next.get(t);if(i)return i;let r=e===this.#i?this.#l(this.#a.step(e.state,t,0,!0)):this.#l(this.#n.step(e.state,t,1,!0));return s<128?(e.ascii||(e.ascii=new Array(128),this.#s+=128),e.ascii[s]=r):e.next.set(t,r),this.#s++,r}},J_=e=>{let t=[];for(let s=e;s.t!=="match";s=s.next){if(s.t==="split"||s.t==="neg")return;t.push(s)}return t},H_=(e,t,s,i)=>{let r=0,n=s,o=-1,a=0;for(;n<i;){let A=e[r];if(A?.t==="star"){o=r++,a=n;continue}let c=t[n];if(A&&(A.t==="any"||A.t==="lit"&&Jp(A,c)||A.t==="class"&&A.re.test(c))){r++,n++;continue}if(o===-1)return!1;r=o+1,n=++a}for(;e[r]?.t==="star";)r++;return r===e.length},Mw=e=>{let t=0,s=0;for(let i of e){let[r,n]=V_(i);t+=r,s+=n}return[t,s]},V_=e=>{if(e.t==="star")return[0,1/0];if(e.t!=="ext")return[1,1];if(e.type==="!")return[0,1/0];let t=e.alts.map(Mw),s=Math.min(...t.map(n=>n[0])),i=Math.max(...t.map(n=>n[1])),r=i===0?0:1/0;return e.type==="@"?[s,i]:e.type==="?"?[0,i]:e.type==="*"?[0,r]:[s,r]},sw=(e,t,s)=>{for(let i=0;i<e.length;i++)if(!Jp(e[i],t[s+i]))return!1;return!0},q_=/[/\ud800-\udfff]/,W_=/[\ud800-\udfff]/,za=e=>W_.test(e)?[...e]:e,iw=e=>{if(e.includes("/"))throw new Error("can only match a single path portion, got: "+e)},rw=class{pattern;options;#e;#t;#s;#r;#i;#n;#a;#c=!1;#l=0;#p=1/0;#u=[];#g=[];#A;#m;#I=!1;#f;#h;constructor(e,t={}){if(e.includes("/"))throw new Error("can only match a single path portion, got pattern: "+e);this.pattern=e,this.options=t;let{nocase:s=!1,noext:i=!1,normalize:r}=t;this.#h=r===!0?"NFC":r||void 0;let n=/^[?*+@]\(\)$/.test(e),o=M_(e,s,i||n,this.#h),[a]=Sw(o,G_(o),0,o.length,!0);this.#h&&(a=Fw(a,this.#h));let[A,c]=L_(a,s);if(this.#e=A,this.#t=c,a.every(l=>l.t==="lit"))this.#i=a.map(l=>l.ch).join(""),s&&(this.#n=Gp(this.#i));else{let l=h=>h.t!=="lit"?void 0:s?{ch:h.ch,re:Gp(h.ch)}:{ch:h.ch};for(let h of a){let d=l(h);if(!d)break;this.#u.push(d)}for(let h=a.length-1;h>=0;h--){let d=l(a[h]);if(!d)break;this.#g.unshift(d)}this.#I=!!(this.#u.length||this.#g.length),s||(this.#A=this.#u.map(h=>h.ch).join(""),this.#m=this.#g.map(h=>h.ch).join("")),this.#a=J_(this.#e)?.slice(this.#u.length,-this.#g.length||void 0),this.#c=!!this.#a?.every(h=>h.t==="star"||h.t==="any");let[u,p]=Mw(a);this.#l=u,this.#p=p}}test(e){return this.#h?(iw(e),this.#C(e.normalize(this.#h))):q_.test(e)?(iw(e),this.#C(e,[...e])):this.#C(e,e)}testUnchecked(e) Line truncated
>>> no match, partial?`,e,l,t,p),l===a))}let h;if(typeof c=="string"?(h=u===c,this.debug("string match",c,u,h)):(h=c.test(u),this.debug("pattern match",c,u,h)),!h)return!1}if(o===a&&n===A)return!0;if(o===a)return s;if(n===A)return o===a-1&&e[o]==="";throw new Error("wtf?")}braceExpand(){return PI(this.pattern,this.options)}parse(e){Ja(e);let t=this.options;if(e==="**")return be;if(e==="")return"";let s,r=null;(s=e.match(DL))?r=t.dot?TL:RL:(s=e.match(QL))?r=(t.nocase?t.dot?wL:IL:t.dot?CL:BL)(s[1]):(s=e.match(FL))?r=(t.nocase?t.dot?UL:SL:t.dot?NL:GL)(s):(s=e.match(bL))?r=t.dot?xL:yL:(s=e.match(vL))&&(r=kL);let i=LI.fromGlob(e,this.options).toMMPattern();return r&&typeof i=="object"&&Reflect.defineProperty(i,"test",{value:r}),i}makeRe(){if(this.regexp||this.regexp===!1)return this.regexp;let e=this.set;if(!e.length)return this.regexp=!1,this.regexp;let t=this.options,s=t.noglobstar?_L:t.dot?YL:OL,r=new Set(t.nocase?["i"]:[]),i=e.map(a=>{let A=a.map(u=>{if(u instanceof RegExp)for(let l of u.flags.split(""))r.add(l);return typeof u=="string"?qL(u):u===be?be:u._src});A.forEach((u,l)=>{let p=A[l+1],g=A[l-1];u!==be||g===be||(g===void 0?p!==void 0&&p!==be?A[l+1]="(?:\\/|"+s+"\\/)?"+p:A[l]=s:p===void 0?A[l-1]=g+"(?:\\/|\\/"+s+")?":p!==be&&(A[l-1]=g+"(?:\\/|\\/"+s+"\\/)"+p,A[l+1]=be))});let c=A.filter(u=>u!==be);if(this.partial&&c.length>=1){let u=[];for(let l=1;l<=c.length;l++)u.push(c.slice(0,l).join("/"));return"(?:"+u.join("|")+")"}return c.join("/")}).join("|"),[o,n]=e.length>1?["(?:",")"]:["",""];i="^"+o+i+n+"$",this.partial&&(i="^(?:\\/|"+o+i.slice(1,-1)+n+")$"),this.negate&&(i="^(?!"+i+").+$");try{this.regexp=new RegExp(i,[...r].join(""))}catch{this.regexp=!1}return this.regexp}slashSplit(e){return this.preserveMultipleSlashes?e.split("/"):this.isWindows&&/^\/\/[^\/]+/.test(e)?["",...e.split(/\/+/)]:e.split(/\/+/)}match(e,t=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return e==="";if(e==="/"&&t)return!0;let s=this.options;this.isWindows&&(e=e.split("\\").join("/"));let r=this.slashSplit(e);this.debug(this.pattern,"split",r);let i=this.set;this.debug(this.pattern,"set",i);let o=r[r.length-1];if(!o)for(let n=r.length-2;!o&&n>=0;n--)o=r[n];for(let n=0;n<i.length;n++){let a=i[n],A=r;if(s.matchBase&&a.length===1&&(A=[o]),this.matchOne(A,a,t))return s.flipNegate?!0:!this.negate}return s.flipNegate?!1:this.negate}static defaults(e){return ye.defaults(e).Minimatch}};ye.AST=LI;ye.Minimatch=fs;ye.escape=_I;ye.unescape=Xr;var WL=typeof performance=="object"&&performance&&typeof performance.now=="function"?performance:Date,VI=new Set,ep=typeof process=="object"&&process?process:{},qI=(e,t,s,r)=>{typeof ep.emitWarning=="function"?ep.emitWarning(e,t,s,r):console.error(`[${s}] ${t}: ${e}`)},Pa=globalThis.AbortController,EI=globalThis.AbortSignal;if(typeof Pa>"u"){EI=class{onabort;_onabort=[];reason;aborted=!1;addEventListener(s,r){this._onabort.push(r)}},Pa=class{constructor(){t()}signal=new EI;abort(s){if(!this.signal.aborted){this.signal.reason=s,this.signal.aborted=!0;for(let r of this.signal._onabort)r(s);this.signal.onabort?.(s)}}};let e=ep.env?.LRU_CACHE_IGNORE_AC_WARNING!=="1",t=()=>{e&&(e=!1,qI("AbortController is not defined. If using lru-cache in node 14, load an AbortController polyfill from the `node-abort-controller` package. A minimal polyfill is provided for use by LRUCache.fetch(), but it should not be relied upon in other contexts (eg, passing it to other APIs that use AbortController/AbortSignal might have undesirable effects). You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.","NO_ABORT_CONTROLLER","ENOTSUP",t))}}var jL=e=>!VI.has(e),ms=e=>e&&e===Math.floor(e)&&e>0&&isFinite(e),WI=e=>ms(e)?e<=Math.pow(2,8)?Uint8Array:e<=Math.pow(2,16)?Uint16Array:e<=Math.pow(2,32)?Uint32Array:e<=Number.MAX_SAFE_INTEGER?Ya:null:null,Ya=class extends Array{constructor(e){super(e),this.fill(0)}},zL=class po{heap;length;static#e=!1;static create(t){let s=WI(t);if(!s)return[];po.#e=!0;let r=new po(t,s);return po.#e=!1,r}constructor(t,s){if(!po.#e)throw new TypeError("instantiate Stack using Stack.create(n)");this.heap=new s(t),this.length=0}push(t){this.heap[this.length++]=t}pop(){return this.heap[--this.length]}},Va=class jI{#e;#t;#i;#s;#r;#A;#a;#c;get perf(){return this.#c}ttl;ttlResolution;ttlAutopurge;updateAgeOnGet;updateAgeOnHas;allowStale;noDisposeOnSet;noUpdateTTL;maxEntrySize;sizeCalculation;noDeleteOnFetchRejection;noDeleteOnStaleGet;allowStaleOnFetchAbort;allowStaleOnFetchRejection;ignoreFetchAbort;#l;#g;#h;#u;#n;#d;#C;#B;#E;#k;#m;#b;#y;#f;#Q;#I;#x;#o;#U;static unsafeExposeInternals(t){return{starts:t.#y,ttls:t.#f,autopurgeTimers:t.#Q,sizes:t.#b,keyMap:t.#h,keyList:t.#u,valList:t.#n,next:t.#d,prev:t.#C,get head(){return t.#B},get tail(){return t.#E},free:t.#k,isBackgroundFetch:s=>t.#p(s),backgroundFetch:(s,r,i,o)=>t.#J(s,r,i,o),moveToTail:s=>t.#G(s),indexes:s=>t.#R(s),rindexes:s=>t.#T(s),isStale:s=>t.#w(s)}}get max(){return this.#e}get maxSize(){return th Line truncated
${s.data.body}`:t.body=s.data.body}return t.body=t.body?this.truncateReleaseNotes(t.body):void 0,this.github.rest.repos.createRelease(t)}async updateRelease(t){if(typeof t.make_latest=="string"&&!["true","false","legacy"].includes(t.make_latest)&&(t.make_latest=void 0),t.generate_release_notes){let s=await this.getReleaseNotes(t);t.generate_release_notes=!1,t.body?t.body=`${t.body} ${r.data.body}`:i.body=r.data.body}return i.body=i.body?this.truncateReleaseNotes(i.body):void 0,i}truncateReleaseNotes(t){return t.substring(0,124999)}async createRelease(t){return this.github.rest.repos.createRelease(await this.prepareReleaseMutation(t))}async updateRelease(t){return this.github.rest.repos.updateRelease(await this.prepareReleaseMutation(t))}async finalizeRelease(t){return await this.github.rest.repos.updateRelease({owner:t.owner,repo:t.repo,release_id:t.release_id,draft:!1,make_latest:t.make_latest,discussion_category_name:t.discussion_category_name})}allReleases(t){let s={per_page:100,...t};return this.github.paginate.iterator(this.github.rest.repos.listReleases.endpoint.merge(s))}async listReleaseAssets(t){return this.github.paginate(this.github.rest.repos.listReleaseAssets,{...t,per_page:100})}async deleteReleaseAsset(t){let{release_id:s,...i}=t;try{await this.github.rest.repos.deleteReleaseAsset(i)}catch(r){if(St(r)!==404)throw r;try{await this.github.request("DELETE /repos/{owner}/{repo}/releases/{release_id}/assets/{asset_id}",t)}catch(n){throw new AggregateError([r,n],`Failed to delete release asset ${t.asset_id}. GitHub endpoint: ${Qs(r)}; release-scoped endpoint: ${Qs(n)}`)}}}async deleteRelease(t){await this.github.rest.repos.deleteRelease(t)}async updateReleaseAsset(t){return await this.github.rest.repos.updateReleaseAsset(t)}async uploadReleaseAsset(t){return this.github.request({method:"POST",url:t.url,headers:{"content-length":`${t.size}`,"content-type":t.mime,authorization:`token ${t.token}`},data:t.data})}},Z2=e=>({name:(0,xb.basename)(e),mime:K2(e),size:(0,wb.statSync)(e).size}),K2=e=>(0,yb.lookup)(e)||"application/octet-stream",Kp=(e,t)=>t.name===e||t.name===Cb(e)||t.label===e,X2=e=>{let t=St(e),s=q2(e),i=kb(e),r=typeof s=="string"&&(/\/releases\/assets\//.test(s)||/\/releases\/\d+\/assets(?:\?|$)/.test(s));return t===404&&(r||typeof i=="string"&&i.includes("update-a-release-asset"))},$2=e=>{let t=St(e),s=vb(e)?.message??kb(e);return t===422&&/immutable release/i.test(String(s))},eY=(e,t)=>t?`Cannot upload asset ${e} to an immutable release. GitHub only allows asset uploads before a release is published, but draft prereleases publish with the release.published event instead of release.prereleased. If you need prereleases with assets on an immutable-release repository, keep the release as a draft with draft: true, then publish it later from that draft and subscribe downstream workflows to release.published.`:`Cannot upload asset ${e} to an immutable release. GitHub only allows asset uploads before a release is published, so upload assets to a draft release before you publish it.`,Sb=async(e,t,s,i,r,n)=>{let[o,a]=e.github_repository.split("/"),{name:A,mime:c,size:l}=Z2(i),u=r.find(g=>Kp(A,g));if(u){if(e.input_overwrite_files===!1)return console.log(`Asset ${A} already exists and overwrite_files is false...`),null;console.log(`\u267B\uFE0F Deleting previously uploaded asset ${A}...`),await t.deleteReleaseAsset({asset_id:u.id||1,owner:o,repo:a,release_id:n})}console.log(`\u2B06\uFE0F Uploading ${A}...`);let p=new URL(s);p.searchParams.append("name",A);let h=async(g,m=3)=>{if(n!==void 0)for(let B=1;B<=m;B++){let b=(await t.listReleaseAssets({owner:o,repo:a,release_id:n})).find(g);if(b)return b;B<m&&await new Promise(U=>setTimeout(U,1e3))}},d=async()=>{let g=await(0,bb.open)(i);try{return await t.uploadReleaseAsset({url:p.toString(),size:l,mime:c,token:e.github_token,data:W2(g)})}finally{await g.close()}},E=async g=>{if(!g.name||g.name===A||!g.id)return g;console.log(`\u270F\uFE0F Restoring asset label to ${A}...`);let m=async B=>{let{data:C}=await t.updateReleaseAsset({owner:o,repo:a,asset_id:B,name:g.name,label:A});return C};try{return await m(g.id)}catch(B){if(St(B)===404&&n!==void 0)try{let b=await h(U=>U.id===g.id||U.name===g.name);if(b)return await m(b.id)}catch(b){console.warn(`error refreshing release assets for ${A}: ${b}`)}return console.warn(`error updating release asset label for ${A}: ${B}`),g}},f=async g=>{let m=g.data;if(g.status!==201)throw new Error(`Failed to upload release asset ${A}. received status code ${g.status}
${s.data.body}`:t.body=s.data.body}return t.body=t.body?this.truncateReleaseNotes(t.body):void 0,this.github.rest.repos.updateRelease(t)}async finalizeRelease(t){return await this.github.rest.repos.updateRelease({owner:t.owner,repo:t.repo,release_id:t.release_id,draft:!1,make_latest:t.make_latest})}allReleases(t){let s={per_page:100,...t};return this.github.paginate.iterator(this.github.rest.repos.listReleases.endpoint.merge(s))}async listReleaseAssets(t){return this.github.paginate(this.github.rest.repos.listReleaseAssets,{...t,per_page:100})}async deleteReleaseAsset(t){await this.github.rest.repos.deleteReleaseAsset(t)}async deleteRelease(t){await this.github.rest.repos.deleteRelease(t)}async updateReleaseAsset(t){return await this.github.rest.repos.updateReleaseAsset(t)}async uploadReleaseAsset(t){return this.github.request({method:"POST",url:t.url,headers:{"content-length":`${t.size}`,"content-type":t.mime,authorization:`token ${t.token}`},data:t.data})}},U_=e=>({name:(0,Dw.basename)(e),mime:N_(e),size:(0,xw.statSync)(e).size}),N_=e=>(0,kw.lookup)(e)||"application/octet-stream",Rw=async(e,t,s,r,i)=>{let[o,n]=e.github_repository.split("/"),{name:a,mime:A,size:c}=U_(r),u=s.match(/\/releases\/(\d+)\/assets/),l=u?Number(u[1]):void 0,p=i.find(({name:E,label:m})=>E===a||E===cp(a)||m===a);if(p){if(e.input_overwrite_files===!1)return console.log(`Asset ${a} already exists and overwrite_files is false...`),null;console.log(`\u267B\uFE0F Deleting previously uploaded asset ${a}...`),await t.deleteReleaseAsset({asset_id:p.id||1,owner:o,repo:n})}console.log(`\u2B06\uFE0F Uploading ${a}...`);let g=new URL(s);g.searchParams.append("name",a);let h=async()=>{let E=await(0,vw.open)(r);try{return await t.uploadReleaseAsset({url:g.toString(),size:c,mime:A,token:e.github_token,data:E.readableWebStream({type:"bytes"})})}finally{await E.close()}};try{let E=await h(),m=E.data;if(E.status!==201)throw new Error(`Failed to upload release asset ${a}. received status code ${E.status}
${m.message} ${m.message}
${JSON.stringify(m.errors)}`);if(m.name&&m.name!==a&&m.id){console.log(`\u270F\uFE0F Restoring asset label to ${a}...`);try{let{data:d}=await t.updateReleaseAsset({owner:o,repo:n,asset_id:m.id,name:m.name,label:a});return console.log(`\u2705 Uploaded ${a}`),d}catch(d){console.warn(`error updating release asset label for ${a}: ${d}`)}}return console.log(`\u2705 Uploaded ${a}`),m}catch(E){let m=E?.status??E?.response?.status,d=E?.response?.data;if(e.input_overwrite_files!==!1&&m===422&&d?.errors?.[0]?.code==="already_exists"&&l!==void 0){console.log(`\u26A0\uFE0F Asset ${a} already exists (race condition), refreshing assets and retrying once...`);let C=(await t.listReleaseAssets({owner:o,repo:n,release_id:l})).find(({name:B})=>B==cp(a));if(C){await t.deleteReleaseAsset({owner:o,repo:n,asset_id:C.id});let B=await h(),b=B.data;if(B.status!==201)throw new Error(`Failed to upload release asset ${a}. received status code ${B.status} ${JSON.stringify(m.errors)}`);let B=await E(m);return console.log(`\u2705 Uploaded ${A}`),B};try{return await f(await d())}catch(g){let m=St(g);if($2(g))throw new Error(eY(A,e.input_prerelease));if(n!==void 0&&X2(g))try{let B=await h(C=>Kp(A,C));if(B)return console.warn(`error updating release asset metadata for ${A}: ${g}. Matching asset is present after refresh; continuing...`),B}catch(B){console.warn(`error refreshing release assets after metadata update failure: ${B}`)}if(e.input_overwrite_files!==!1&&m===422&&Rb(g,"already_exists")&&n!==void 0){console.log(`\u26A0\uFE0F Asset ${A} already exists (race condition), refreshing assets and retrying once...`);let C=(await t.listReleaseAssets({owner:o,repo:a,release_id:n})).find(b=>Kp(A,b));if(C)return await t.deleteReleaseAsset({owner:o,repo:a,release_id:n,asset_id:C.id}),await f(await d())}throw g}},$p=async(e,t,s=3)=>{if(s<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[i,r]=e.github_repository.split("/"),n=Zp(e.input_tag_name)||(Sn(e.github_ref)?e.github_ref.replace("refs/tags/",""):""),o=e.input_discussion_category_name,a=e.input_generate_release_notes,A=e.input_previous_tag;a&&A&&console.log(`\u{1F4DD} Generating release notes using previous tag ${A}`);let c;try{c=await Fb(t,i,r,n,s)}catch(l){if(St(l)===404){let u=z2(i,r,l);throw console.log(`\u26A0\uFE0F ${u}`),new Xp(u,l)}throw console.log(`\u26A0\uFE0F Unexpected error fetching GitHub release for tag ${e.github_ref}: ${l}`),l}if(c===void 0)return await Ib(n,e,t,i,r,o,a,s,A);try{let l=c;console.log(`Found release ${l.name} (with id=${l.id})`);let u=l.id,p;e.input_target_commitish&&e.input_target_commitish!==l.target_commitish?(console.log(`Updating commit from "${l.target_commitish}" to "${e.input_target_commitish}"`),p=e.input_target_commitish):p=l.target_commitish;let h=n,d=e.input_name||l.name||n,E=zp(e)||"",f=l.body||"",g;e.input_append_body&&E&&f?g=f+`
${b.message} `+E:g=E||f;let m=e.input_prerelease!==void 0?e.input_prerelease:l.prerelease,B=e.input_make_latest;return{release:(await t.updateRelease({owner:i,repo:r,release_id:u,tag_name:h,target_commitish:p,name:d,body:g,draft:l.draft,prerelease:m,discussion_category_name:o,generate_release_notes:a,make_latest:B,previous_tag_name:A})).data,created:!1}}catch(l){if(l instanceof fA)throw l;if(St(l)!==404)throw console.log(`\u26A0\uFE0F Unexpected error fetching GitHub release for tag ${e.github_ref}: ${l}`),l;return await Ib(n,e,t,i,r,o,a,s,A)}},eg=async(e,t,s,i=!1,r=3)=>{if(e.input_draft===!0||s.draft===!1)return s;if(r<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[n,o]=e.github_repository.split("/");try{let{data:a}=await t.finalizeRelease({owner:n,repo:o,release_id:s.id,make_latest:e.input_make_latest,discussion_category_name:e.input_discussion_category_name});return a}catch(a){if(console.warn(`error finalizing release: ${a}`),i&&s.draft&&rY(a)){let A=!1;try{console.log(`\u{1F9F9} Deleting draft release ${s.id} for tag ${s.tag_name} because tag creation is blocked by repository rules...`),await t.deleteRelease({owner:n,repo:o,release_id:s.id}),A=!0}catch(l){console.warn(`error deleting orphan draft release ${s.id}: ${l}`)}let c=A?`Deleted draft release ${s.id} to avoid leaving an orphaned draft release.`:`Failed to delete draft release ${s.id}; manual cleanup may still be required.`;throw new Error(`Tag creation for ${s.tag_name} is blocked by repository rules. ${c}`)}return console.log(`retrying... (${r-1} retries remaining)`),eg(e,t,s,i,r-1)}},tg=async(e,t,s,i=3)=>{if(i<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[r,n]=e.github_repository.split("/");try{return await t.listReleaseAssets({owner:r,repo:n,release_id:s.id})}catch(o){return console.warn(`error listing assets of release: ${o}`),console.log(`retrying... (${i-1} retries remaining)`),tg(e,t,s,i-1)}};async function Fb(e,t,s,i,r=1){try{let{data:o}=await e.getReleaseByTag({owner:t,repo:s,tag:i});return o}catch(o){if(St(o)!==404)throw o}if(r<=0)return;let n=Math.max(1,r);for(let o=0;o<n;o+=1){let a=await Mb(e,t,s,i),A=Gb(a,void 0);if(A)return A;o<n-1&&await Nb(Ub)}}var Ub=1e3,tY=2;async function Nb(e){await new Promise(t=>setTimeout(t,e))}async function Mb(e,t,s,i){let r=[],n=0;for await(let o of e.allReleases({owner:t,repo:s}))if(r.push(...o.data.filter(a=>a.tag_name===i)),n+=1,n>=tY)break;return r}function Gb(e,t){return t&&e.some(s=>s.id===t.id)||e.length===0?t:[...e].sort((s,i)=>s.draft!==i.draft?Number(s.draft)-Number(i.draft):s.id-i.id)[0]}async function sY(e,t,s,i,r,n){if(!(n.id===r||!n.draft||n.assets.length>0))try{console.log(`\u{1F9F9} Removing duplicate draft release ${n.id} for tag ${i}...`),await e.deleteRelease({owner:t,repo:s,release_id:n.id})}catch(o){console.warn(`error deleting duplicate release ${n.id}: ${o}`)}}async function iY(e,t,s,i,r,n){let o=Math.max(n,1);for(let a=1;a<=o;a+=1){let A;try{A=await Fb(e,t,s,i,0)}catch(u){console.warn(`error reloading release for tag ${i}: ${u}`)}let c=[];try{c=await Mb(e,t,s,i)}catch(u){console.warn(`error listing recent releases for tag ${i}: ${u}`)}let l=Gb(c,A);if(l){l.id!==r.id&&console.log(`\u21AA\uFE0F Using release ${l.id} for tag ${i} instead of duplicate draft ${r.id}`);let u=c.find(p=>p.id===r.id);return u&&await sY(e,t,s,i,l.id,u),l}a<o&&(console.log(`Release ${r.id} is not yet discoverable by tag ${i}, retrying... (${o-a} retries remaining)`),await Nb(Ub))}return console.log(`\u26A0\uFE0F Continuing with newly created release ${r.id} because tag ${i} is still not discoverable`),r}async function Ib(e,t,s,i,r,n,o,a,A){let c=e,l=t.input_name||e,u=zp(t),p=t.input_prerelease,h=p===!0?t.input_draft===!0:!0,d=t.input_target_commitish,E=t.input_make_latest,f="";d&&(f=` using commit "${d}"`),console.log(`\u{1F469}\u200D\u{1F3ED} Creating new GitHub release for tag ${c}${f}...`);try{let g=await s.createRelease({owner:i,repo:r,tag_name:c,name:l,body:u,draft:h,prerelease:p,target_commitish:d,discussion_category_name:n,generate_release_notes:o,make_latest:E,previous_tag_name:A}),m=await iY(s,i,r,c,g.data,a);return{release:m,created:m.id===g.data.id}}catch(g){let m=St(g);switch(console.log(`\u26A0\uFE0F GitHub release failed with status: ${m}`),console.log(Qs(g)),m){case 403:throw console.log("Skip retry \u2014 your GitHub token/PAT does not have the required permission to create a release"),g;case 404:let B=j2(i,r,n,g);throw console.log(`Skip retry \u2014 ${B}`),new fA(B,g);case 422:if(Rb(g,"already_exists"))console.log("\u26A0\uFE0F Release already exists (race condition detected), retrying to find and update existing release...");else throw console.log("Skip retry - validation failed"),g;break}return console.log(`retrying... (${a-1} retries remaining)`),$p(t,s,a-1)}}function rY(e){return St(e)!==422?!1:Db(e).some(t=>{let s=Tt(t);return s?.field==="pre_receive"&&typeof s.message=="string"&&s.message.includes( Line truncated
${JSON.stringify(b.errors)}`);return console.log(`\u2705 Uploaded ${a}`),b}}throw E}},lp=async(e,t,s=3)=>{if(s<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[r,i]=e.github_repository.split("/"),o=Ap(e.input_tag_name)||(fo(e.github_ref)?e.github_ref.replace("refs/tags/",""):""),n=e.input_discussion_category_name,a=e.input_generate_release_notes;try{let A=await Tw(t,r,i,o);if(A===void 0)return await yw(o,e,t,r,i,n,a,s);let c=A;console.log(`Found release ${c.name} (with id=${c.id})`);let u=c.id,l;e.input_target_commitish&&e.input_target_commitish!==c.target_commitish?(console.log(`Updating commit from "${c.target_commitish}" to "${e.input_target_commitish}"`),l=e.input_target_commitish):l=c.target_commitish;let p=o,g=e.input_name||c.name||o,h=ap(e)||"",E=c.body||"",m;e.input_append_body&&h&&E?m=E+`
`+h:m=h||E;let d=e.input_prerelease!==void 0?e.input_prerelease:c.prerelease,f=e.input_make_latest;return{release:(await t.updateRelease({owner:r,repo:i,release_id:u,tag_name:p,target_commitish:l,name:g,body:m,draft:c.draft,prerelease:d,discussion_category_name:n,generate_release_notes:a,make_latest:f})).data,created:!1}}catch(A){if(A.status!==404)throw console.log(`\u26A0\uFE0F Unexpected error fetching GitHub release for tag ${e.github_ref}: ${A}`),A;return await yw(o,e,t,r,i,n,a,s)}},up=async(e,t,s,r=!1,i=3)=>{if(e.input_draft===!0||s.draft===!1)return s;if(i<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[o,n]=e.github_repository.split("/");try{let{data:a}=await t.finalizeRelease({owner:o,repo:n,release_id:s.id,make_latest:e.input_make_latest});return a}catch(a){if(console.warn(`error finalizing release: ${a}`),r&&s.draft&&P_(a)){let A=!1;try{console.log(`\u{1F9F9} Deleting draft release ${s.id} for tag ${s.tag_name} because tag creation is blocked by repository rules...`),await t.deleteRelease({owner:o,repo:n,release_id:s.id}),A=!0}catch(u){console.warn(`error deleting orphan draft release ${s.id}: ${u}`)}let c=A?`Deleted draft release ${s.id} to avoid leaving an orphaned draft release.`:`Failed to delete draft release ${s.id}; manual cleanup may still be required.`;throw new Error(`Tag creation for ${s.tag_name} is blocked by repository rules. ${c}`)}return console.log(`retrying... (${i-1} retries remaining)`),up(e,t,s,r,i-1)}},pp=async(e,t,s,r=3)=>{if(r<=0)throw console.log("\u274C Too many retries. Aborting..."),new Error("Too many retries.");let[i,o]=e.github_repository.split("/");try{return await t.listReleaseAssets({owner:i,repo:o,release_id:s.id})}catch(n){return console.warn(`error listing assets of release: ${n}`),console.log(`retrying... (${r-1} retries remaining)`),pp(e,t,s,r-1)}};async function Tw(e,t,s,r){try{let{data:i}=await e.getReleaseByTag({owner:t,repo:s,tag:r});return i}catch(i){if(i.status===404)return;throw i}}var G_=1e3,M_=2;async function L_(e){await new Promise(t=>setTimeout(t,e))}async function __(e,t,s,r){let i=[],o=0;for await(let n of e.allReleases({owner:t,repo:s}))if(i.push(...n.data.filter(a=>a.tag_name===r)),o+=1,o>=M_)break;return i}function Y_(e,t){return t&&e.some(s=>s.id===t.id)||e.length===0?t:[...e].sort((s,r)=>s.draft!==r.draft?Number(s.draft)-Number(r.draft):s.id-r.id)[0]}async function O_(e,t,s,r,i,o){let n=Array.from(new Map(o.map(a=>[a.id,a])).values());for(let a of n)if(!(a.id===i||!a.draft||a.assets.length>0))try{console.log(`\u{1F9F9} Removing duplicate draft release ${a.id} for tag ${r}...`),await e.deleteRelease({owner:t,repo:s,release_id:a.id})}catch(A){console.warn(`error deleting duplicate release ${a.id}: ${A}`)}}async function J_(e,t,s,r,i,o){let n=Math.max(o,1);for(let a=1;a<=n;a+=1){let A;try{A=await Tw(e,t,s,r)}catch(l){console.warn(`error reloading release for tag ${r}: ${l}`)}let c=[];try{c=await __(e,t,s,r)}catch(l){console.warn(`error listing recent releases for tag ${r}: ${l}`)}let u=Y_(c,A);if(u)return u.id!==i.id&&console.log(`\u21AA\uFE0F Using release ${u.id} for tag ${r} instead of duplicate draft ${i.id}`),await O_(e,t,s,r,u.id,[i,...c]),u;a<n&&(console.log(`Release ${i.id} is not yet discoverable by tag ${r}, retrying... (${n-a} retries remaining)`),await L_(G_))}return console.log(`\u26A0\uFE0F Continuing with newly created release ${i.id} because tag ${r} is still not discoverable`),i}async function yw(e,t,s,r,i,o,n,a){let A=e,c=t.input_name||e,u=ap(t),l=t.input_prerelease,p=l===!0?t.input_draft===!0:!0,g=t.input_target_commitish,h=t.input_make_latest,E="";g&&(E=` using commit "${g}"`),console.log(`\u{1F469}\u200D\u{1F3ED} Creating new GitHub release for tag ${A}${E}...`);try{let m=await s.createRelease({owner:r,repo:i,tag_name:A,name:c,body:u,draft:p,prerelease:l,target_commitish:g,discussion_category_name:o,generate_release_notes:n,make_latest:h}),d=await J_(s,r,i,A,m.data,a);return{release:d,created:d.id===m.data.id}}catch(m){switch(console.log(`\u26A0\uFE0F GitHub release failed with status: ${m.status}`),console.log(`${JSON.stringify(m.response.data)}`),m.status){case 403:throw console.log("Skip retry \u2014 your GitHub token/PAT does not have the required permission to create a release"),m;case 404:throw console.log("Skip retry - discussion category mismatch"),m;case 422:if(m.response?.data?.errors?.[0]?.code==="already_exists")console.log("\u26A0\uFE0F Release already exists (race condition detected), retrying to find and update existing release...");else throw console.log("Skip retry - validation failed"),m;break}return console.log(`retrying... (${a-1} retries remaining)`),lp(t,s,a-1)}}function P_(e){let t=e?.response?.data?.errors;return!Array.isArray(t)||e?.status!==422?!1:t.some(({field:s,message:r})=>s==="pre_receive"&&typeof r=="string"&&r.includes("creations being restricted"))}var Fw=require("process");async function H_(){try{let e=Cw(Fw.env);if(!e.input_tag_name&&!fo(e Line truncated
/*! Bundled license information: /*! Bundled license information:
undici/lib/web/fetch/body.js: undici/lib/web/fetch/body.js:
+963 -996
View File
File diff suppressed because it is too large. Load diff
+18 -18
View File
@@ -1,17 +1,17 @@
{ {
"name": "action-gh-release", "name": "action-gh-release",
"version": "2.5.3", "version": "3.0.4",
"private": true, "private": true,
"description": "GitHub Action for creating GitHub Releases", "description": "GitHub Action for creating GitHub Releases",
"main": "lib/main.js", "main": "lib/main.js",
"scripts": { "scripts": {
"build": "esbuild src/main.ts --bundle --platform=node --format=cjs --target=node20 --outfile=dist/index.js --minify", "build": "esbuild src/main.ts --bundle --platform=node --format=cjs --target=node24 --outfile=dist/index.js --minify",
"build-debug": "esbuild src/main.ts --bundle --platform=node --format=cjs --target=node20 --outfile=dist/index.js --sourcemap --keep-names", "build-debug": "esbuild src/main.ts --bundle --platform=node --format=cjs --target=node24 --outfile=dist/index.js --sourcemap --keep-names",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"test": "vitest --coverage", "test": "vitest --coverage",
"fmt": "prettier --write \"src/**/*.ts\" \"__tests__/**/*.ts\"", "fmt": "prettier --write \"src/**/*.ts\" \"__tests__/**/*.ts\"",
"fmtcheck": "prettier --check \"src/**/*.ts\" \"__tests__/**/*.ts\"", "fmtcheck": "prettier --check \"src/**/*.ts\" \"__tests__/**/*.ts\"",
"updatetag": "git tag -d v2 && git push origin :v2 && git tag -a v2 -m '' && git push origin v2" "updatetag": "git tag -d v3 >/dev/null 2>&1 || true; git push origin :v3 >/dev/null 2>&1 || true; git tag -a v3 -m '' && git push origin v3"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -21,24 +21,24 @@
"actions" "actions"
], ],
"author": "softprops", "author": "softprops",
"engines": {
"node": ">=24"
},
"dependencies": { "dependencies": {
"@actions/core": "^3.0.0", "@actions/core": "^3.0.1",
"@actions/github": "^9.0.0", "@actions/github": "^9.1.1",
"@octokit/plugin-retry": "^8.1.0", "@octokit/plugin-retry": "^8.1.1",
"@octokit/plugin-throttling": "^11.0.3", "@octokit/plugin-throttling": "^11.0.5",
"glob": "^13.0.6", "glob": "^14.0.3",
"mime-types": "^3.0.2" "mime-types": "^3.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/glob": "^9.0.0",
"@types/mime-types": "^3.0.1", "@types/mime-types": "^3.0.1",
"@types/node": "^20.19.37", "@types/node": "^24",
"@vitest/coverage-v8": "^4.1.0", "@vitest/coverage-v8": "^5.0.5",
"esbuild": "^0.27.3", "esbuild": "^0.28.2",
"prettier": "3.8.1", "prettier": "3.9.10",
"ts-node": "^10.9.2", "typescript": "^7.0.2",
"typescript": "^5.9.3", "vitest": "^5.0.0"
"typescript-formatter": "^7.2.2",
"vitest": "^4.0.4"
} }
} }
+429 -172
View File
@@ -1,12 +1,63 @@
import { GitHub } from '@actions/github/lib/utils'; import { GitHub } from '@actions/github/lib/utils';
import { statSync } from 'fs'; import { statSync } from 'fs';
import { open } from 'fs/promises'; import { open, type FileHandle } from 'fs/promises';
import { lookup } from 'mime-types'; import { lookup } from 'mime-types';
import { basename } from 'path'; import { basename } from 'path';
import { alignAssetName, Config, isTag, normalizeTagName, releaseBody } from './util'; import { alignAssetName, Config, errorMessage, isTag, normalizeTagName, releaseBody } from './util';
type GitHub = InstanceType<typeof GitHub>; type GitHub = InstanceType<typeof GitHub>;
type UploadChunk = ArrayBuffer | Uint8Array<ArrayBufferLike>;
type UploadBody = ReadableStream<Uint8Array<ArrayBufferLike>>;
type UnknownRecord = Record<string, unknown>;
const asRecord = (value: unknown): UnknownRecord | undefined =>
typeof value === 'object' && value !== null ? (value as UnknownRecord) : undefined;
const getErrorStatus = (error: unknown): number | undefined => {
const errorRecord = asRecord(error);
if (typeof errorRecord?.status === 'number') {
return errorRecord.status;
}
const response = asRecord(errorRecord?.response);
return typeof response?.status === 'number' ? response.status : undefined;
};
const getResponseData = (error: unknown): UnknownRecord | undefined => {
const response = asRecord(asRecord(error)?.response);
return asRecord(response?.data);
};
const getErrorMessage = (error: unknown): string | undefined => {
const message = asRecord(error)?.message;
return typeof message === 'string' ? message : undefined;
};
const getRequestUrl = (error: unknown): string | undefined => {
const request = asRecord(asRecord(error)?.request);
return typeof request?.url === 'string' ? request.url : undefined;
};
const getValidationErrors = (error: unknown): unknown[] => {
const errors = getResponseData(error)?.errors;
return Array.isArray(errors) ? errors : [];
};
const hasValidationErrorCode = (error: unknown, code: string): boolean =>
asRecord(getValidationErrors(error)[0])?.code === code;
const fileUploadStream = (fileHandle: FileHandle): UploadBody => {
const source = fileHandle.readableWebStream() as ReadableStream<UploadChunk>;
return source.pipeThrough(
new TransformStream<UploadChunk, Uint8Array<ArrayBufferLike>>({
transform(chunk, controller) {
controller.enqueue(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk));
},
}),
);
};
export interface ReleaseAsset { export interface ReleaseAsset {
name: string; name: string;
mime: string; mime: string;
@@ -31,10 +82,15 @@ export interface ReleaseResult {
created: boolean; created: boolean;
} }
export interface Releaser { type ReleaseNotesParams = {
getReleaseByTag(params: { owner: string; repo: string; tag: string }): Promise<{ data: Release }>; owner: string;
repo: string;
tag_name: string;
target_commitish: string | undefined;
previous_tag_name?: string;
};
createRelease(params: { type ReleaseMutationParams = {
owner: string; owner: string;
repo: string; repo: string;
tag_name: string; tag_name: string;
@@ -46,28 +102,64 @@ export interface Releaser {
discussion_category_name: string | undefined; discussion_category_name: string | undefined;
generate_release_notes: boolean | undefined; generate_release_notes: boolean | undefined;
make_latest: 'true' | 'false' | 'legacy' | undefined; make_latest: 'true' | 'false' | 'legacy' | undefined;
}): Promise<{ data: Release }>; previous_tag_name?: string;
};
updateRelease(params: { class ReleaseCreationError extends Error {
owner: string; readonly status = 404;
repo: string;
constructor(message: string, cause: unknown) {
super(message, { cause });
this.name = 'ReleaseCreationError';
}
}
class ReleaseAccessError extends Error {
readonly status = 404;
constructor(message: string, cause: unknown) {
super(message, { cause });
this.name = 'ReleaseAccessError';
}
}
const repositoryAccessGuidance = (owner: string, repo: string): string =>
`Verify that ${owner}/${repo} exists under the expected owner, the token can access it, the repository is selected when using a fine-grained PAT, and the token has Contents: write permission.`;
const releaseCreation404Message = (
owner: string,
repo: string,
discussionCategory: string | undefined,
error: unknown,
): string => {
const discussionGuidance = discussionCategory
? ` Also verify that Discussions and the requested category "${discussionCategory}" are enabled.`
: '';
return `GitHub returned 404 while creating the release. ${repositoryAccessGuidance(owner, repo)}${discussionGuidance} GitHub response: ${errorMessage(error)}`;
};
const releaseLookup404Message = (owner: string, repo: string, error: unknown): string =>
`GitHub returned 404 while checking existing releases. ${repositoryAccessGuidance(owner, repo)} GitHub response: ${errorMessage(error)}`;
export interface Releaser {
getReleaseByTag(params: { owner: string; repo: string; tag: string }): Promise<{ data: Release }>;
createRelease(params: ReleaseMutationParams): Promise<{ data: Release }>;
updateRelease(
params: ReleaseMutationParams & {
release_id: number; release_id: number;
tag_name: string;
target_commitish: string; target_commitish: string;
name: string; },
body: string | undefined; ): Promise<{ data: Release }>;
draft: boolean | undefined;
prerelease: boolean | undefined;
discussion_category_name: string | undefined;
generate_release_notes: boolean | undefined;
make_latest: 'true' | 'false' | 'legacy' | undefined;
}): Promise<{ data: Release }>;
finalizeRelease(params: { finalizeRelease(params: {
owner: string; owner: string;
repo: string; repo: string;
release_id: number; release_id: number;
make_latest: 'true' | 'false' | 'legacy' | undefined; make_latest: 'true' | 'false' | 'legacy' | undefined;
discussion_category_name: string | undefined;
}): Promise<{ data: Release }>; }): Promise<{ data: Release }>;
allReleases(params: { owner: string; repo: string }): AsyncIterable<{ data: Release[] }>; allReleases(params: { owner: string; repo: string }): AsyncIterable<{ data: Release[] }>;
@@ -78,7 +170,12 @@ export interface Releaser {
release_id: number; release_id: number;
}): Promise<Array<{ id: number; name: string; label?: string | null; [key: string]: any }>>; }): Promise<Array<{ id: number; name: string; label?: string | null; [key: string]: any }>>;
deleteReleaseAsset(params: { owner: string; repo: string; asset_id: number }): Promise<void>; deleteReleaseAsset(params: {
owner: string;
repo: string;
release_id: number;
asset_id: number;
}): Promise<void>;
deleteRelease(params: { owner: string; repo: string; release_id: number }): Promise<void>; deleteRelease(params: { owner: string; repo: string; release_id: number }): Promise<void>;
@@ -95,7 +192,7 @@ export interface Releaser {
size: number; size: number;
mime: string; mime: string;
token: string; token: string;
data: any; data: UploadBody;
}): Promise<{ status: number; data: any }>; }): Promise<{ status: number; data: any }>;
} }
@@ -113,12 +210,7 @@ export class GitHubReleaser implements Releaser {
return this.github.rest.repos.getReleaseByTag(params); return this.github.rest.repos.getReleaseByTag(params);
} }
async getReleaseNotes(params: { async getReleaseNotes(params: ReleaseNotesParams): Promise<{
owner: string;
repo: string;
tag_name: string;
target_commitish: string | undefined;
}): Promise<{
data: { data: {
name: string; name: string;
body: string; body: string;
@@ -127,75 +219,55 @@ export class GitHubReleaser implements Releaser {
return await this.github.rest.repos.generateReleaseNotes(params); return await this.github.rest.repos.generateReleaseNotes(params);
} }
private async prepareReleaseMutation<T extends ReleaseMutationParams>(
params: T,
): Promise<Omit<T, 'previous_tag_name'>> {
const { previous_tag_name, ...releaseParams } = params;
if (
typeof releaseParams.make_latest === 'string' &&
!['true', 'false', 'legacy'].includes(releaseParams.make_latest)
) {
releaseParams.make_latest = undefined;
}
if (releaseParams.generate_release_notes) {
const releaseNotes = await this.getReleaseNotes({
owner: releaseParams.owner,
repo: releaseParams.repo,
tag_name: releaseParams.tag_name,
target_commitish: releaseParams.target_commitish,
previous_tag_name,
});
releaseParams.generate_release_notes = false;
if (releaseParams.body) {
releaseParams.body = `${releaseParams.body}\n\n${releaseNotes.data.body}`;
} else {
releaseParams.body = releaseNotes.data.body;
}
}
releaseParams.body = releaseParams.body
? this.truncateReleaseNotes(releaseParams.body)
: undefined;
return releaseParams;
}
truncateReleaseNotes(input: string): string { truncateReleaseNotes(input: string): string {
// release notes can be a maximum of 125000 characters // release notes can be a maximum of 125000 characters
const githubNotesMaxCharLength = 125000; const githubNotesMaxCharLength = 125000;
return input.substring(0, githubNotesMaxCharLength - 1); return input.substring(0, githubNotesMaxCharLength - 1);
} }
async createRelease(params: { async createRelease(params: ReleaseMutationParams): Promise<{ data: Release }> {
owner: string; return this.github.rest.repos.createRelease(await this.prepareReleaseMutation(params));
repo: string;
tag_name: string;
name: string;
body: string | undefined;
draft: boolean | undefined;
prerelease: boolean | undefined;
target_commitish: string | undefined;
discussion_category_name: string | undefined;
generate_release_notes: boolean | undefined;
make_latest: 'true' | 'false' | 'legacy' | undefined;
}): Promise<{ data: Release }> {
if (
typeof params.make_latest === 'string' &&
!['true', 'false', 'legacy'].includes(params.make_latest)
) {
params.make_latest = undefined;
}
if (params.generate_release_notes) {
const releaseNotes = await this.getReleaseNotes(params);
params.generate_release_notes = false;
if (params.body) {
params.body = `${params.body}\n\n${releaseNotes.data.body}`;
} else {
params.body = releaseNotes.data.body;
}
}
params.body = params.body ? this.truncateReleaseNotes(params.body) : undefined;
return this.github.rest.repos.createRelease(params);
} }
async updateRelease(params: { async updateRelease(
owner: string; params: ReleaseMutationParams & {
repo: string;
release_id: number; release_id: number;
tag_name: string;
target_commitish: string; target_commitish: string;
name: string; },
body: string | undefined; ): Promise<{ data: Release }> {
draft: boolean | undefined; return this.github.rest.repos.updateRelease(await this.prepareReleaseMutation(params));
prerelease: boolean | undefined;
discussion_category_name: string | undefined;
generate_release_notes: boolean | undefined;
make_latest: 'true' | 'false' | 'legacy' | undefined;
}): Promise<{ data: Release }> {
if (
typeof params.make_latest === 'string' &&
!['true', 'false', 'legacy'].includes(params.make_latest)
) {
params.make_latest = undefined;
}
if (params.generate_release_notes) {
const releaseNotes = await this.getReleaseNotes(params);
params.generate_release_notes = false;
if (params.body) {
params.body = `${params.body}\n\n${releaseNotes.data.body}`;
} else {
params.body = releaseNotes.data.body;
}
}
params.body = params.body ? this.truncateReleaseNotes(params.body) : undefined;
return this.github.rest.repos.updateRelease(params);
} }
async finalizeRelease(params: { async finalizeRelease(params: {
@@ -203,6 +275,7 @@ export class GitHubReleaser implements Releaser {
repo: string; repo: string;
release_id: number; release_id: number;
make_latest: 'true' | 'false' | 'legacy' | undefined; make_latest: 'true' | 'false' | 'legacy' | undefined;
discussion_category_name: string | undefined;
}) { }) {
return await this.github.rest.repos.updateRelease({ return await this.github.rest.repos.updateRelease({
owner: params.owner, owner: params.owner,
@@ -210,6 +283,7 @@ export class GitHubReleaser implements Releaser {
release_id: params.release_id, release_id: params.release_id,
draft: false, draft: false,
make_latest: params.make_latest, make_latest: params.make_latest,
discussion_category_name: params.discussion_category_name,
}); });
} }
@@ -234,9 +308,31 @@ export class GitHubReleaser implements Releaser {
async deleteReleaseAsset(params: { async deleteReleaseAsset(params: {
owner: string; owner: string;
repo: string; repo: string;
release_id: number;
asset_id: number; asset_id: number;
}): Promise<void> { }): Promise<void> {
await this.github.rest.repos.deleteReleaseAsset(params); const { release_id, ...githubParams } = params;
try {
await this.github.rest.repos.deleteReleaseAsset(githubParams);
} catch (error: unknown) {
if (getErrorStatus(error) !== 404) {
throw error;
}
try {
await this.github.request(
'DELETE /repos/{owner}/{repo}/releases/{release_id}/assets/{asset_id}',
params,
);
} catch (fallbackError: unknown) {
throw new AggregateError(
[error, fallbackError],
`Failed to delete release asset ${params.asset_id}. GitHub endpoint: ${errorMessage(
error,
)}; release-scoped endpoint: ${errorMessage(fallbackError)}`,
);
}
}
} }
async deleteRelease(params: { owner: string; repo: string; release_id: number }): Promise<void> { async deleteRelease(params: { owner: string; repo: string; release_id: number }): Promise<void> {
@@ -258,7 +354,7 @@ export class GitHubReleaser implements Releaser {
size: number; size: number;
mime: string; mime: string;
token: string; token: string;
data: any; data: UploadBody;
}): Promise<{ status: number; data: any }> { }): Promise<{ status: number; data: any }> {
return this.github.request({ return this.github.request({
method: 'POST', method: 'POST',
@@ -285,23 +381,55 @@ export const mimeOrDefault = (path: string): string => {
return lookup(path) || 'application/octet-stream'; return lookup(path) || 'application/octet-stream';
}; };
const releaseAssetMatchesName = (
name: string,
asset: { name: string; label?: string | null },
): boolean => asset.name === name || asset.name === alignAssetName(name) || asset.label === name;
const isReleaseAssetUpdateNotFound = (error: unknown): boolean => {
const errorStatus = getErrorStatus(error);
const requestUrl = getRequestUrl(error);
const message = getErrorMessage(error);
const isReleaseAssetRequest =
typeof requestUrl === 'string' &&
(/\/releases\/assets\//.test(requestUrl) || /\/releases\/\d+\/assets(?:\?|$)/.test(requestUrl));
return (
errorStatus === 404 &&
(isReleaseAssetRequest ||
(typeof message === 'string' && message.includes('update-a-release-asset')))
);
};
const isImmutableReleaseAssetUploadFailure = (error: unknown): boolean => {
const errorStatus = getErrorStatus(error);
const message = getResponseData(error)?.message ?? getErrorMessage(error);
return errorStatus === 422 && /immutable release/i.test(String(message));
};
const immutableReleaseAssetUploadMessage = (
name: string,
prerelease: boolean | undefined,
): string =>
prerelease
? `Cannot upload asset ${name} to an immutable release. GitHub only allows asset uploads before a release is published, but draft prereleases publish with the release.published event instead of release.prereleased. If you need prereleases with assets on an immutable-release repository, keep the release as a draft with draft: true, then publish it later from that draft and subscribe downstream workflows to release.published.`
: `Cannot upload asset ${name} to an immutable release. GitHub only allows asset uploads before a release is published, so upload assets to a draft release before you publish it.`;
export const upload = async ( export const upload = async (
config: Config, config: Config,
releaser: Releaser, releaser: Releaser,
url: string, url: string,
path: string, path: string,
currentAssets: Array<{ id: number; name: string; label?: string | null }>, currentAssets: Array<{ id: number; name: string; label?: string | null }>,
releaseId: number,
): Promise<any> => { ): Promise<any> => {
const [owner, repo] = config.github_repository.split('/'); const [owner, repo] = config.github_repository.split('/');
const { name, mime, size } = asset(path); const { name, mime, size } = asset(path);
const releaseIdMatch = url.match(/\/releases\/(\d+)\/assets/);
const releaseId = releaseIdMatch ? Number(releaseIdMatch[1]) : undefined;
const currentAsset = currentAssets.find( const currentAsset = currentAssets.find(
// note: GitHub renames asset filenames that have special characters, non-alphanumeric characters, and leading or trailing periods. The "List release assets" endpoint lists the renamed filenames. // GitHub can rewrite uploaded asset names, so compare against both the raw name
// due to this renaming we need to be mindful when we compare the file name we're uploading with a name github may already have rewritten for logical comparison // GitHub returns and the restored label we set when available.
// see https://docs.github.com/en/rest/releases/assets?apiVersion=2022-11-28#upload-a-release-asset (currentAsset) => releaseAssetMatchesName(name, currentAsset),
({ name: currentName, label: currentLabel }) =>
currentName === name || currentName === alignAssetName(name) || currentLabel === name,
); );
if (currentAsset) { if (currentAsset) {
if (config.input_overwrite_files === false) { if (config.input_overwrite_files === false) {
@@ -313,12 +441,39 @@ export const upload = async (
asset_id: currentAsset.id || 1, asset_id: currentAsset.id || 1,
owner, owner,
repo, repo,
release_id: releaseId,
}); });
} }
} }
console.log(`⬆️ Uploading ${name}...`); console.log(`⬆️ Uploading ${name}...`);
const endpoint = new URL(url); const endpoint = new URL(url);
endpoint.searchParams.append('name', name); endpoint.searchParams.append('name', name);
const findReleaseAsset = async (
matches: (asset: { id: number; name: string; label?: string | null }) => boolean,
attempts: number = 3,
) => {
if (releaseId === undefined) {
return undefined;
}
for (let attempt = 1; attempt <= attempts; attempt++) {
const latestAssets = await releaser.listReleaseAssets({
owner,
repo,
release_id: releaseId,
});
const latestAsset = latestAssets.find(matches);
if (latestAsset) {
return latestAsset;
}
if (attempt < attempts) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return undefined;
};
const uploadAsset = async () => { const uploadAsset = async () => {
const fh = await open(path); const fh = await open(path);
try { try {
@@ -327,15 +482,61 @@ export const upload = async (
size, size,
mime, mime,
token: config.github_token, token: config.github_token,
data: fh.readableWebStream({ type: 'bytes' }), data: fileUploadStream(fh),
}); });
} finally { } finally {
await fh.close(); await fh.close();
} }
}; };
const maybeRestoreAssetLabel = async (uploadedAsset: {
id?: number;
name?: string;
label?: string | null;
[key: string]: any;
}) => {
if (!uploadedAsset.name || uploadedAsset.name === name || !uploadedAsset.id) {
return uploadedAsset;
}
console.log(`✏️ Restoring asset label to ${name}...`);
const updateAssetLabel = async (assetId: number) => {
const { data } = await releaser.updateReleaseAsset({
owner,
repo,
asset_id: assetId,
name: uploadedAsset.name!,
label: name,
});
return data;
};
try { try {
const resp = await uploadAsset(); return await updateAssetLabel(uploadedAsset.id);
} catch (error: unknown) {
const errorStatus = getErrorStatus(error);
if (errorStatus === 404 && releaseId !== undefined) {
try {
const latestAsset = await findReleaseAsset(
(currentAsset) =>
currentAsset.id === uploadedAsset.id || currentAsset.name === uploadedAsset.name,
);
if (latestAsset) {
return await updateAssetLabel(latestAsset.id);
}
} catch (refreshError) {
console.warn(`error refreshing release assets for ${name}: ${refreshError}`);
}
}
console.warn(`error updating release asset label for ${name}: ${error}`);
return uploadedAsset;
}
};
const handleUploadedAsset = async (resp: { status: number; data: any }) => {
const json = resp.data; const json = resp.data;
if (resp.status !== 201) { if (resp.status !== 201) {
throw new Error( throw new Error(
@@ -344,33 +545,43 @@ export const upload = async (
}\n${json.message}\n${JSON.stringify(json.errors)}`, }\n${json.message}\n${JSON.stringify(json.errors)}`,
); );
} }
if (json.name && json.name !== name && json.id) { const assetWithLabel = await maybeRestoreAssetLabel(json);
console.log(`✏️ Restoring asset label to ${name}...`); console.log(`✅ Uploaded ${name}`);
return assetWithLabel;
};
try { try {
const { data } = await releaser.updateReleaseAsset({ return await handleUploadedAsset(await uploadAsset());
owner, } catch (error: unknown) {
repo, const errorStatus = getErrorStatus(error);
asset_id: json.id,
name: json.name, if (isImmutableReleaseAssetUploadFailure(error)) {
label: name, throw new Error(immutableReleaseAssetUploadMessage(name, config.input_prerelease));
}); }
console.log(`✅ Uploaded ${name}`);
return data; if (releaseId !== undefined && isReleaseAssetUpdateNotFound(error)) {
} catch (error) { try {
console.warn(`error updating release asset label for ${name}: ${error}`); const latestAsset = await findReleaseAsset((currentAsset) =>
releaseAssetMatchesName(name, currentAsset),
);
if (latestAsset) {
console.warn(
`error updating release asset metadata for ${name}: ${error}. Matching asset is present after refresh; continuing...`,
);
return latestAsset;
}
} catch (refreshError) {
console.warn(
`error refreshing release assets after metadata update failure: ${refreshError}`,
);
} }
} }
console.log(`✅ Uploaded ${name}`);
return json;
} catch (error: any) {
const errorStatus = error?.status ?? error?.response?.status;
const errorData = error?.response?.data;
// Handle race conditions across concurrent workflows uploading the same asset. // Handle race conditions across concurrent workflows uploading the same asset.
if ( if (
config.input_overwrite_files !== false && config.input_overwrite_files !== false &&
errorStatus === 422 && errorStatus === 422 &&
errorData?.errors?.[0]?.code === 'already_exists' && hasValidationErrorCode(error, 'already_exists') &&
releaseId !== undefined releaseId !== undefined
) { ) {
console.log( console.log(
@@ -381,26 +592,17 @@ export const upload = async (
repo, repo,
release_id: releaseId, release_id: releaseId,
}); });
const latestAsset = latestAssets.find( const latestAsset = latestAssets.find((currentAsset) =>
({ name: currentName }) => currentName == alignAssetName(name), releaseAssetMatchesName(name, currentAsset),
); );
if (latestAsset) { if (latestAsset) {
await releaser.deleteReleaseAsset({ await releaser.deleteReleaseAsset({
owner, owner,
repo, repo,
release_id: releaseId,
asset_id: latestAsset.id, asset_id: latestAsset.id,
}); });
const retryResp = await uploadAsset(); return await handleUploadedAsset(await uploadAsset());
const retryJson = retryResp.data;
if (retryResp.status !== 201) {
throw new Error(
`Failed to upload release asset ${name}. received status code ${
retryResp.status
}\n${retryJson.message}\n${JSON.stringify(retryJson.errors)}`,
);
}
console.log(`✅ Uploaded ${name}`);
return retryJson;
} }
} }
@@ -425,8 +627,25 @@ export const release = async (
const discussion_category_name = config.input_discussion_category_name; const discussion_category_name = config.input_discussion_category_name;
const generate_release_notes = config.input_generate_release_notes; const generate_release_notes = config.input_generate_release_notes;
const previous_tag_name = config.input_previous_tag;
if (generate_release_notes && previous_tag_name) {
console.log(`📝 Generating release notes using previous tag ${previous_tag_name}`);
}
let _release: Release | undefined;
try { try {
const _release: Release | undefined = await findTagFromReleases(releaser, owner, repo, tag); _release = await findTagFromReleases(releaser, owner, repo, tag, maxRetries);
} catch (error) {
if (getErrorStatus(error) === 404) {
const diagnostic = releaseLookup404Message(owner, repo, error);
console.log(`⚠️ ${diagnostic}`);
throw new ReleaseAccessError(diagnostic, error);
}
console.log(
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
);
throw error;
}
if (_release === undefined) { if (_release === undefined) {
return await createRelease( return await createRelease(
@@ -438,9 +657,11 @@ export const release = async (
discussion_category_name, discussion_category_name,
generate_release_notes, generate_release_notes,
maxRetries, maxRetries,
previous_tag_name,
); );
} }
try {
let existingRelease: Release = _release!; let existingRelease: Release = _release!;
console.log(`Found release ${existingRelease.name} (with id=${existingRelease.id})`); console.log(`Found release ${existingRelease.name} (with id=${existingRelease.id})`);
@@ -491,13 +712,17 @@ export const release = async (
discussion_category_name, discussion_category_name,
generate_release_notes, generate_release_notes,
make_latest, make_latest,
previous_tag_name,
}); });
return { return {
release: release.data, release: release.data,
created: false, created: false,
}; };
} catch (error) { } catch (error) {
if (error.status !== 404) { if (error instanceof ReleaseCreationError) {
throw error;
}
if (getErrorStatus(error) !== 404) {
console.log( console.log(
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`, `⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
); );
@@ -513,6 +738,7 @@ export const release = async (
discussion_category_name, discussion_category_name,
generate_release_notes, generate_release_notes,
maxRetries, maxRetries,
previous_tag_name,
); );
} }
}; };
@@ -549,6 +775,7 @@ export const finalizeRelease = async (
repo, repo,
release_id: release.id, release_id: release.id,
make_latest: config.input_make_latest, make_latest: config.input_make_latest,
discussion_category_name: config.input_discussion_category_name,
}); });
return data; return data;
@@ -623,14 +850,16 @@ export const listReleaseAssets = async (
/** /**
* Finds a release by tag name. * Finds a release by tag name.
* *
* Uses the direct getReleaseByTag API for O(1) lookup instead of iterating * Uses the direct getReleaseByTag API for O(1) lookup. Because GitHub does not
* through all releases. This also avoids GitHub's API pagination limit of * expose draft releases through that endpoint, a 404 falls back to a bounded
* 10000 results which would cause failures for repositories with many releases. * scan of recent releases and briefly retries in case the listing is not yet
* consistent.
* *
* @param releaser - The GitHub API wrapper for release operations * @param releaser - The GitHub API wrapper for release operations
* @param owner - The owner of the repository * @param owner - The owner of the repository
* @param repo - The name of the repository * @param repo - The name of the repository
* @param tag - The tag name to search for * @param tag - The tag name to search for
* @param listingAttempts - The maximum number of listing attempts after a direct 404
* @returns The release with the given tag name, or undefined if no release with that tag name is found * @returns The release with the given tag name, or undefined if no release with that tag name is found
*/ */
export async function findTagFromReleases( export async function findTagFromReleases(
@@ -638,20 +867,37 @@ export async function findTagFromReleases(
owner: string, owner: string,
repo: string, repo: string,
tag: string, tag: string,
listingAttempts: number = 1,
): Promise<Release | undefined> { ): Promise<Release | undefined> {
try { try {
const { data: release } = await releaser.getReleaseByTag({ owner, repo, tag }); const { data: release } = await releaser.getReleaseByTag({ owner, repo, tag });
return release; return release;
} catch (error) { } catch (error) {
// Release not found (404) or other error - return undefined to allow creation if (getErrorStatus(error) !== 404) {
if (error.status === 404) {
return undefined;
}
// Re-throw unexpected errors
throw error; throw error;
} }
} }
if (listingAttempts <= 0) {
return undefined;
}
const attempts = Math.max(1, listingAttempts);
for (let attempt = 0; attempt < attempts; attempt += 1) {
const recentReleases = await recentReleasesByTag(releaser, owner, repo, tag);
const canonicalRelease = pickCanonicalRelease(recentReleases, undefined);
if (canonicalRelease) {
return canonicalRelease;
}
if (attempt < attempts - 1) {
await sleep(CREATED_RELEASE_DISCOVERY_RETRY_DELAY_MS);
}
}
return undefined;
}
const CREATED_RELEASE_DISCOVERY_RETRY_DELAY_MS = 1000; const CREATED_RELEASE_DISCOVERY_RETRY_DELAY_MS = 1000;
const RECENT_RELEASE_SCAN_PAGES = 2; const RECENT_RELEASE_SCAN_PAGES = 2;
@@ -701,33 +947,31 @@ function pickCanonicalRelease(
})[0]; })[0];
} }
async function cleanupDuplicateDraftReleases( async function cleanupCreatedDuplicateDraftRelease(
releaser: Releaser, releaser: Releaser,
owner: string, owner: string,
repo: string, repo: string,
tag: string, tag: string,
canonicalReleaseId: number, canonicalReleaseId: number,
releases: Release[], createdRelease: Release,
): Promise<void> { ): Promise<void> {
const uniqueReleases = Array.from( if (
new Map(releases.map((release) => [release.id, release])).values(), createdRelease.id === canonicalReleaseId ||
); !createdRelease.draft ||
createdRelease.assets.length > 0
for (const duplicate of uniqueReleases) { ) {
if (duplicate.id === canonicalReleaseId || !duplicate.draft || duplicate.assets.length > 0) { return;
continue;
} }
try { try {
console.log(`🧹 Removing duplicate draft release ${duplicate.id} for tag ${tag}...`); console.log(`🧹 Removing duplicate draft release ${createdRelease.id} for tag ${tag}...`);
await releaser.deleteRelease({ await releaser.deleteRelease({
owner, owner,
repo, repo,
release_id: duplicate.id, release_id: createdRelease.id,
}); });
} catch (error) { } catch (error) {
console.warn(`error deleting duplicate release ${duplicate.id}: ${error}`); console.warn(`error deleting duplicate release ${createdRelease.id}: ${error}`);
}
} }
} }
@@ -744,7 +988,7 @@ async function canonicalizeCreatedRelease(
for (let attempt = 1; attempt <= attempts; attempt += 1) { for (let attempt = 1; attempt <= attempts; attempt += 1) {
let releaseByTag: Release | undefined; let releaseByTag: Release | undefined;
try { try {
releaseByTag = await findTagFromReleases(releaser, owner, repo, tag); releaseByTag = await findTagFromReleases(releaser, owner, repo, tag, 0);
} catch (error) { } catch (error) {
console.warn(`error reloading release for tag ${tag}: ${error}`); console.warn(`error reloading release for tag ${tag}: ${error}`);
} }
@@ -764,10 +1008,19 @@ async function canonicalizeCreatedRelease(
); );
} }
await cleanupDuplicateDraftReleases(releaser, owner, repo, tag, canonicalRelease.id, [ const refreshedCreatedRelease = recentReleases.find(
createdRelease, (release) => release.id === createdRelease.id,
...recentReleases, );
]); if (refreshedCreatedRelease) {
await cleanupCreatedDuplicateDraftRelease(
releaser,
owner,
repo,
tag,
canonicalRelease.id,
refreshedCreatedRelease,
);
}
return canonicalRelease; return canonicalRelease;
} }
@@ -796,6 +1049,7 @@ async function createRelease(
discussion_category_name: string | undefined, discussion_category_name: string | undefined,
generate_release_notes: boolean | undefined, generate_release_notes: boolean | undefined,
maxRetries: number, maxRetries: number,
previous_tag_name: string | undefined,
): Promise<ReleaseResult> { ): Promise<ReleaseResult> {
const tag_name = tag; const tag_name = tag;
const name = config.input_name || tag; const name = config.input_name || tag;
@@ -822,6 +1076,7 @@ async function createRelease(
discussion_category_name, discussion_category_name,
generate_release_notes, generate_release_notes,
make_latest, make_latest,
previous_tag_name,
}); });
const canonicalRelease = await canonicalizeCreatedRelease( const canonicalRelease = await canonicalizeCreatedRelease(
releaser, releaser,
@@ -835,12 +1090,13 @@ async function createRelease(
release: canonicalRelease, release: canonicalRelease,
created: canonicalRelease.id === createdRelease.data.id, created: canonicalRelease.id === createdRelease.data.id,
}; };
} catch (error) { } catch (error: unknown) {
const errorStatus = getErrorStatus(error);
// presume a race with competing matrix runs // presume a race with competing matrix runs
console.log(`⚠️ GitHub release failed with status: ${error.status}`); console.log(`⚠️ GitHub release failed with status: ${errorStatus}`);
console.log(`${JSON.stringify(error.response.data)}`); console.log(errorMessage(error));
switch (error.status) { switch (errorStatus) {
case 403: case 403:
console.log( console.log(
'Skip retry — your GitHub token/PAT does not have the required permission to create a release', 'Skip retry — your GitHub token/PAT does not have the required permission to create a release',
@@ -848,13 +1104,13 @@ async function createRelease(
throw error; throw error;
case 404: case 404:
console.log('Skip retry - discussion category mismatch'); const diagnostic = releaseCreation404Message(owner, repo, discussion_category_name, error);
throw error; console.log(`Skip retry — ${diagnostic}`);
throw new ReleaseCreationError(diagnostic, error);
case 422: case 422:
// Check if this is a race condition with "already_exists" error // Check if this is a race condition with "already_exists" error
const errorData = error.response?.data; if (hasValidationErrorCode(error, 'already_exists')) {
if (errorData?.errors?.[0]?.code === 'already_exists') {
console.log( console.log(
'⚠️ Release already exists (race condition detected), retrying to find and update existing release...', '⚠️ Release already exists (race condition detected), retrying to find and update existing release...',
); );
@@ -871,16 +1127,17 @@ async function createRelease(
} }
} }
function isTagCreationBlockedError(error: any): boolean { function isTagCreationBlockedError(error: unknown): boolean {
const errors = error?.response?.data?.errors; if (getErrorStatus(error) !== 422) {
if (!Array.isArray(errors) || error?.status !== 422) {
return false; return false;
} }
return errors.some( return getValidationErrors(error).some((validationError) => {
({ field, message }: { field?: string; message?: string }) => const errorRecord = asRecord(validationError);
field === 'pre_receive' && return (
typeof message === 'string' && errorRecord?.field === 'pre_receive' &&
message.includes('creations being restricted'), typeof errorRecord.message === 'string' &&
errorRecord.message.includes('creations being restricted')
); );
});
} }
+2 -113
View File
@@ -1,114 +1,3 @@
import { setFailed, setOutput } from '@actions/core'; import { run } from './run';
import { getOctokit } from '@actions/github';
import { GitHubReleaser, release, finalizeRelease, upload, listReleaseAssets } from './github';
import { isTag, parseConfig, paths, unmatchedPatterns, uploadUrl } from './util';
import { env } from 'process'; void run();
async function run() {
try {
const config = parseConfig(env);
if (!config.input_tag_name && !isTag(config.github_ref) && !config.input_draft) {
throw new Error(`⚠️ GitHub Releases requires a tag`);
}
if (config.input_files) {
const patterns = unmatchedPatterns(config.input_files, config.input_working_directory);
patterns.forEach((pattern) => {
if (config.input_fail_on_unmatched_files) {
throw new Error(`⚠️ Pattern '${pattern}' does not match any files.`);
} else {
console.warn(`🤔 Pattern '${pattern}' does not match any files.`);
}
});
if (patterns.length > 0 && config.input_fail_on_unmatched_files) {
throw new Error(`⚠️ There were unmatched files`);
}
}
// const oktokit = GitHub.plugin(
// require("@octokit/plugin-throttling"),
// require("@octokit/plugin-retry")
// );
const gh = getOctokit(config.github_token, {
//new oktokit(
throttle: {
onRateLimit: (retryAfter, options) => {
console.warn(`Request quota exhausted for request ${options.method} ${options.url}`);
if (options.request.retryCount === 0) {
// only retries once
console.log(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
onAbuseLimit: (retryAfter, options) => {
// does not retry, only logs a warning
console.warn(`Abuse detected for request ${options.method} ${options.url}`);
},
},
});
//);
const releaser = new GitHubReleaser(gh);
const releaseResult = await release(config, releaser);
let rel = releaseResult.release;
const releaseWasCreated = releaseResult.created;
let uploadedAssetIds: Set<number> = new Set();
if (config.input_files && config.input_files.length > 0) {
const files = paths(config.input_files, config.input_working_directory);
if (files.length == 0) {
if (config.input_fail_on_unmatched_files) {
throw new Error(`⚠️ ${config.input_files} does not include a valid file.`);
} else {
console.warn(`🤔 ${config.input_files} does not include a valid file.`);
}
}
const currentAssets = rel.assets;
const uploadFile = async (path: string) => {
const json = await upload(config, releaser, uploadUrl(rel.upload_url), path, currentAssets);
return json ? (json.id as number) : undefined;
};
let results: (number | undefined)[];
if (!config.input_preserve_order) {
results = await Promise.all(files.map(uploadFile));
} else {
results = [];
for (const path of files) {
results.push(await uploadFile(path));
}
}
uploadedAssetIds = new Set(results.filter((id): id is number => id !== undefined));
}
console.log('Finalizing release...');
rel = await finalizeRelease(config, releaser, rel, releaseWasCreated);
// Draft releases use temporary "untagged-..." URLs for assets.
// URLs will be changed to correct ones once the release is published.
console.log('Getting assets list...');
{
let assets: any[] = [];
if (uploadedAssetIds.size > 0) {
const updatedAssets = await listReleaseAssets(config, releaser, rel);
assets = updatedAssets
.filter((a) => uploadedAssetIds.has(a.id))
.map((a) => {
const { uploader, ...rest } = a;
return rest;
});
}
setOutput('assets', assets);
}
console.log(`🎉 Release ready at ${rel.html_url}`);
setOutput('url', rel.html_url);
setOutput('id', rel.id.toString());
setOutput('upload_url', rel.upload_url);
} catch (error) {
setFailed(error.message);
}
}
run();
+104
View File
@@ -0,0 +1,104 @@
import { setFailed, setOutput } from '@actions/core';
import { getOctokit } from '@actions/github';
import { env } from 'process';
import { GitHubReleaser, release, finalizeRelease, upload, listReleaseAssets } from './github';
import { errorMessage, isTag, parseConfig, paths, unmatchedPatterns, uploadUrl } from './util';
export async function run(): Promise<void> {
try {
const config = parseConfig(env);
if (!config.input_tag_name && !isTag(config.github_ref) && !config.input_draft) {
throw new Error(`⚠️ GitHub Releases requires a tag`);
}
if (config.input_files) {
const patterns = unmatchedPatterns(config.input_files, config.input_working_directory);
patterns.forEach((pattern) => {
if (config.input_fail_on_unmatched_files) {
throw new Error(`⚠️ Pattern '${pattern}' does not match any files.`);
} else {
console.warn(`🤔 Pattern '${pattern}' does not match any files.`);
}
});
}
const gh = getOctokit(config.github_token, {
throttle: {
onRateLimit: (retryAfter, options) => {
console.warn(`Request quota exhausted for request ${options.method} ${options.url}`);
if (options.request.retryCount === 0) {
console.log(`Retrying after ${retryAfter} seconds!`);
return true;
}
},
onAbuseLimit: (_retryAfter, options) => {
console.warn(`Abuse detected for request ${options.method} ${options.url}`);
},
},
});
const releaser = new GitHubReleaser(gh);
const releaseResult = await release(config, releaser);
let rel = releaseResult.release;
const releaseWasCreated = releaseResult.created;
let uploadedAssetIds: Set<number> = new Set();
if (config.input_files && config.input_files.length > 0) {
const files = paths(config.input_files, config.input_working_directory);
if (files.length === 0) {
if (config.input_fail_on_unmatched_files) {
throw new Error(`⚠️ ${config.input_files} does not include a valid file.`);
} else {
console.warn(`🤔 ${config.input_files} does not include a valid file.`);
}
}
const currentAssets = rel.assets;
const uploadFile = async (path: string) => {
const json = await upload(
config,
releaser,
uploadUrl(rel.upload_url),
path,
currentAssets,
rel.id,
);
return json ? (json.id as number) : undefined;
};
let results: (number | undefined)[];
if (!config.input_preserve_order) {
results = await Promise.all(files.map(uploadFile));
} else {
results = [];
for (const path of files) {
results.push(await uploadFile(path));
}
}
uploadedAssetIds = new Set(results.filter((id): id is number => id !== undefined));
}
console.log('Finalizing release...');
rel = await finalizeRelease(config, releaser, rel, releaseWasCreated);
// Draft releases use temporary "untagged-..." URLs for assets.
// URLs will be changed to correct ones once the release is published.
console.log('Getting assets list...');
let assets: any[] = [];
if (uploadedAssetIds.size > 0) {
const updatedAssets = await listReleaseAssets(config, releaser, rel);
assets = updatedAssets
.filter((asset) => uploadedAssetIds.has(asset.id))
.map((asset) => {
const { uploader, ...rest } = asset;
return rest;
});
}
setOutput('assets', assets);
console.log(`🎉 Release ready at ${rel.html_url}`);
setOutput('url', rel.html_url);
setOutput('id', rel.id.toString());
setOutput('upload_url', rel.upload_url);
} catch (error: unknown) {
setFailed(errorMessage(error));
}
}
+20
View File
@@ -23,10 +23,29 @@ export interface Config {
input_target_commitish?: string; input_target_commitish?: string;
input_discussion_category_name?: string; input_discussion_category_name?: string;
input_generate_release_notes?: boolean; input_generate_release_notes?: boolean;
input_previous_tag?: string;
input_append_body?: boolean; input_append_body?: boolean;
input_make_latest: 'true' | 'false' | 'legacy' | undefined; input_make_latest: 'true' | 'false' | 'legacy' | undefined;
} }
export const errorMessage = (error: unknown): string => {
if (error instanceof Error) {
return error.message;
}
if (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof error.message === 'string'
) {
return error.message;
}
if (error === null || error === undefined) {
return 'Unknown error';
}
return String(error);
};
export const uploadUrl = (url: string): string => { export const uploadUrl = (url: string): string => {
const templateMarkerPos = url.indexOf('{'); const templateMarkerPos = url.indexOf('{');
if (templateMarkerPos > -1) { if (templateMarkerPos > -1) {
@@ -114,6 +133,7 @@ export const parseConfig = (env: Env): Config => {
input_target_commitish: env.INPUT_TARGET_COMMITISH || undefined, input_target_commitish: env.INPUT_TARGET_COMMITISH || undefined,
input_discussion_category_name: env.INPUT_DISCUSSION_CATEGORY_NAME || undefined, input_discussion_category_name: env.INPUT_DISCUSSION_CATEGORY_NAME || undefined,
input_generate_release_notes: env.INPUT_GENERATE_RELEASE_NOTES == 'true', input_generate_release_notes: env.INPUT_GENERATE_RELEASE_NOTES == 'true',
input_previous_tag: env.INPUT_PREVIOUS_TAG?.trim() || undefined,
input_append_body: env.INPUT_APPEND_BODY == 'true', input_append_body: env.INPUT_APPEND_BODY == 'true',
input_make_latest: parseMakeLatest(env.INPUT_MAKE_LATEST), input_make_latest: parseMakeLatest(env.INPUT_MAKE_LATEST),
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"useUnknownInCatchVariables": false, "useUnknownInCatchVariables": true,
/* Basic Options */ /* Basic Options */
// "incremental": true, /* Enable incremental compilation */ // "incremental": true, /* Enable incremental compilation */
"target": "es2022", "target": "es2022",
+8 -1
View File
@@ -4,7 +4,14 @@ export default defineConfig({
test: { test: {
environment: 'node', environment: 'node',
coverage: { coverage: {
reporter: ['text', 'lcov'], reporter: ['text', 'json-summary', 'lcov'],
include: ['src/**/*.ts'],
thresholds: {
statements: 94,
branches: 90,
functions: 95,
lines: 94,
},
}, },
include: ['__tests__/**/*.ts'], include: ['__tests__/**/*.ts'],
}, },