Compare commits

..

54 Commits

Author SHA1 Message Date
github-actions[bot]
b02a30ac89 Changelog for Release server-2024-02-01 (#9935)
* Update Changelog

* Update CHANGELOG.md

---------

Co-authored-by: release[bot] <actions@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2024-02-01 19:07:43 +00:00
dependabot[bot]
2588797365 chore(deps): bump smol-toml from 1.1.3 to 1.1.4; test [PythonVersionFromToml DynamicToml] (#9925)
* chore(deps): bump smol-toml from 1.1.3 to 1.1.4

Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.1.3 to 1.1.4.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](https://github.com/squirrelchat/smol-toml/compare/v1.1.3...v1.1.4)

---
updated-dependencies:
- dependency-name: smol-toml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* encode ° symbol in test

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chris48s <git@chris-shaw.dev>
2024-01-29 11:29:23 +00:00
chris48s
4319367c40 migrate some services from examples to openApi (#9904) 2024-01-29 11:05:40 +00:00
chris48s
af19335c5a migrate some services from examples to openApi (#9915) 2024-01-29 11:05:35 +00:00
dependabot[bot]
90d2a230ba chore(deps): bump simple-icons from 11.1.0 to 11.2.0 (#9928)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 11.1.0 to 11.2.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/11.1.0...11.2.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 15:24:17 +00:00
dependabot[bot]
5861f9a4dc chore(deps-dev): bump @typescript-eslint/parser from 6.19.0 to 6.19.1 (#9926)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.19.0 to 6.19.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.19.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 15:02:43 +00:00
dependabot[bot]
74c96387b9 chore(deps): bump @sentry/node from 7.92.0 to 7.98.0 (#9924)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.92.0 to 7.98.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.92.0...7.98.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 14:55:00 +00:00
dependabot[bot]
271dd9c81e chore(deps-dev): bump eslint-plugin-jsdoc from 48.0.2 to 48.0.4 (#9927)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 48.0.2 to 48.0.4.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v48.0.2...v48.0.4)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 14:54:32 +00:00
dependabot[bot]
6041859fae chore(deps): bump actions/dependency-review-action from 3 to 4 (#9920)
Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3 to 4.
- [Release notes](https://github.com/actions/dependency-review-action/releases)
- [Commits](https://github.com/actions/dependency-review-action/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/dependency-review-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-24 20:41:23 +00:00
chris48s
1b91ef57c1 migrate some services from examples to openApi part 39; affects [maven gradle] (#9902)
* allow redirectors to define a openApi property

* migrate some services from examples to openApi

* Update services/gradle-plugin-portal/gradle-plugin-portal.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* Update services/gradle-plugin-portal/gradle-plugin-portal.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* Update services/maven-central/maven-central.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* Update services/maven-central/maven-central.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* Update services/maven-metadata/maven-metadata.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* Update services/maven-metadata/maven-metadata.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

---------

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>
2024-01-24 20:36:32 +00:00
chris48s
bc41c409ff migrate some services from examples to openApi (#9903) 2024-01-24 20:23:12 +00:00
dependabot[bot]
609775ee25 chore(deps): bump actions/cache from 3 to 4 (#9921)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-24 20:22:58 +00:00
Filip Grudzień
e032f3d4b7 feat: added up_message and down_message to [uptimerobotstatus] (#9662)
* feat: added up_message and down_message into uptimerobot-status [8816-issue]

* implement up/down color params, add query params to docs

---------

Co-authored-by: chris48s <git@chris-shaw.dev>
2024-01-23 19:05:13 +00:00
dependabot[bot]
560f79df59 chore(deps-dev): bump c8 from 9.0.0 to 9.1.0 (#9910)
Bumps [c8](https://github.com/bcoe/c8) from 9.0.0 to 9.1.0.
- [Release notes](https://github.com/bcoe/c8/releases)
- [Changelog](https://github.com/bcoe/c8/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bcoe/c8/compare/v9.0.0...v9.1.0)

---
updated-dependencies:
- dependency-name: c8
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:24:57 +00:00
dependabot[bot]
9c3927ea33 chore(deps): bump simple-icons from 11.0.0 to 11.1.0 (#9912)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 11.0.0 to 11.1.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/11.0.0...11.1.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:22:16 +00:00
dependabot[bot]
5ad1e670b2 chore(deps): bump joi from 17.11.0 to 17.12.0 (#9914)
Bumps [joi](https://github.com/hapijs/joi) from 17.11.0 to 17.12.0.
- [Commits](https://github.com/hapijs/joi/compare/v17.11.0...v17.12.0)

---
updated-dependencies:
- dependency-name: joi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:18:26 +00:00
dependabot[bot]
5dd6014cbc chore(deps-dev): bump nock from 13.4.0 to 13.5.0 (#9906)
Bumps [nock](https://github.com/nock/nock) from 13.4.0 to 13.5.0.
- [Release notes](https://github.com/nock/nock/releases)
- [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nock/nock/compare/v13.4.0...v13.5.0)

---
updated-dependencies:
- dependency-name: nock
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:16:27 +00:00
dependabot[bot]
085a9baa80 chore(deps-dev): bump prettier from 3.1.1 to 3.2.4 (#9908)
Bumps [prettier](https://github.com/prettier/prettier) from 3.1.1 to 3.2.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.1.1...3.2.4)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:10:39 +00:00
dependabot[bot]
2129fe5915 chore(deps-dev): bump cypress from 13.6.2 to 13.6.3 (#9909)
Bumps [cypress](https://github.com/cypress-io/cypress) from 13.6.2 to 13.6.3.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/cypress/compare/v13.6.2...v13.6.3)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:10:24 +00:00
dependabot[bot]
c53f15ba9a chore(deps-dev): bump @typescript-eslint/parser from 6.18.1 to 6.19.0 (#9911)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.18.1 to 6.19.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.19.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:09:52 +00:00
dependabot[bot]
3d115ead27 chore(deps-dev): bump nodemon from 3.0.2 to 3.0.3 (#9913)
Bumps [nodemon](https://github.com/remy/nodemon) from 3.0.2 to 3.0.3.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.0.2...v3.0.3)

---
updated-dependencies:
- dependency-name: nodemon
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:09:35 +00:00
dependabot[bot]
5e35dd1e43 chore(deps-dev): bump tsd from 0.30.3 to 0.30.4 (#9907)
Bumps [tsd](https://github.com/tsdjs/tsd) from 0.30.3 to 0.30.4.
- [Release notes](https://github.com/tsdjs/tsd/releases)
- [Commits](https://github.com/tsdjs/tsd/compare/v0.30.3...v0.30.4)

---
updated-dependencies:
- dependency-name: tsd
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-20 14:01:02 +00:00
Andrea Bonari
c5943c0f2d Add [Hangar] Badges (#9800)
* Add Hangar service

* fix linting

* Added required changes

* Fix inconsistent openApi pattern

* Change hangar host
2024-01-16 20:16:04 +00:00
dependabot[bot]
0c18daa278 chore(deps-dev): bump follow-redirects from 1.15.2 to 1.15.4 (#9891)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-14 10:27:48 +00:00
dependabot[bot]
179466ccba chore(deps): bump simple-icons from 10.4.0 to 11.0.0 (#9898)
* chore(deps): bump simple-icons from 10.4.0 to 11.0.0

Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 10.4.0 to 11.0.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/10.4.0...11.0.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* blog post

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chris48s <git@chris-shaw.dev>
2024-01-13 18:58:08 +00:00
dependabot[bot]
bd92f99532 chore(deps-dev): bump chai from 4.3.10 to 4.4.1 (#9900)
Bumps [chai](https://github.com/chaijs/chai) from 4.3.10 to 4.4.1.
- [Release notes](https://github.com/chaijs/chai/releases)
- [Changelog](https://github.com/chaijs/chai/blob/main/History.md)
- [Commits](https://github.com/chaijs/chai/compare/v4.3.10...v4.4.1)

---
updated-dependencies:
- dependency-name: chai
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 18:53:53 +00:00
dependabot[bot]
c1671ec1ae chore(deps): bump fast-xml-parser from 4.3.2 to 4.3.3 (#9897)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.3.2...v4.3.3)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 18:40:28 +00:00
dependabot[bot]
632067fc9e chore(deps-dev): bump @typescript-eslint/parser from 6.17.0 to 6.18.1 (#9896)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.17.0 to 6.18.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.18.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 18:32:22 +00:00
dependabot[bot]
f739cf66d5 chore(deps): bump config from 3.3.9 to 3.3.10 (#9895)
Bumps [config](https://github.com/node-config/node-config) from 3.3.9 to 3.3.10.
- [Release notes](https://github.com/node-config/node-config/releases)
- [Changelog](https://github.com/node-config/node-config/blob/master/History.md)
- [Commits](https://github.com/node-config/node-config/compare/v3.3.9...v3.3.10)

---
updated-dependencies:
- dependency-name: config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 18:32:17 +00:00
dependabot[bot]
ed814132cd chore(deps-dev): bump eslint-plugin-chai-friendly from 0.7.2 to 0.7.4 (#9893)
Bumps [eslint-plugin-chai-friendly](https://github.com/ihordiachenko/eslint-plugin-chai-friendly) from 0.7.2 to 0.7.4.
- [Release notes](https://github.com/ihordiachenko/eslint-plugin-chai-friendly/releases)
- [Commits](https://github.com/ihordiachenko/eslint-plugin-chai-friendly/compare/v0.7.2...v0.7.4)

---
updated-dependencies:
- dependency-name: eslint-plugin-chai-friendly
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 18:32:12 +00:00
chris48s
5fb78c50ef migrate examples to openApi part 38; affects [github] (#9889)
* migrate some services from examples to openApi

* Update services/github/github-size.service.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

---------

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>
2024-01-13 18:31:59 +00:00
chris48s
5fa3435841 [crates] tidyup (#9883)
* fix downloads service test

* remove handling of 200 OK with an error body

* remove obsolete tests
2024-01-13 18:25:40 +00:00
chris48s
7e8b85ecd0 small docs fix: defs --> prestart (#9887)
running `npm run defs` omits the BASE_URL= env var
2024-01-13 18:24:33 +00:00
chris48s
dc84d09732 sort categories by title (except core) (#9888) 2024-01-13 18:24:18 +00:00
Sepehr Safari
4a55c3eaa4 Add Support for [Nostr] Followers (#9870)
* add nostr followers with tests

* fix broken test

* fix http error handler

* rename route from nostr to nostr-band

* remove unnecessary test cases

* edit test expectation

* validate data schema ensuring exact one key

* remove unavailable named logo

* migrate examples to openApi

* Update services/nostr-band/nostr-band-followers.tester.js

* remove unused import

---------

Co-authored-by: chris48s <chris48s@users.noreply.github.com>
Co-authored-by: chris48s <git@chris-shaw.dev>
2024-01-11 19:35:03 +00:00
Joe Clack
bfcbaea116 replace experimental API usage with newly available v1 API (#9886)
resolves badges/shields#9837
2024-01-09 16:36:11 +00:00
CanisHelix
79dd749608 Update [Gitea] defaults to gitea.com (#9872)
* fix(gitea): set gitea.com as default, update tests and message

* fix(gitea): removed required flag

* test(gitea): migrate tests to try.gitea.io

* fix(test): fix auth test nock

* fix(ci): revert tests to codeberg, update description with better verbiage, rename documenation to description

* doc(api): reference flagship url for swagger information in comments
2024-01-08 14:58:59 +00:00
Sky
57aaaad7fe [crates] MSRV Badge (#9871)
Crates.io MSRV: `/crates/msrv/:crate`
Crates.io MSRV (version): `/crates/msrv/:crate/:version`
2024-01-07 20:54:27 +00:00
dependabot[bot]
16d0ebbe7b chore(deps-dev): bump clsx from 2.0.0 to 2.1.0 (#9880)
Bumps [clsx](https://github.com/lukeed/clsx) from 2.0.0 to 2.1.0.
- [Release notes](https://github.com/lukeed/clsx/releases)
- [Commits](https://github.com/lukeed/clsx/compare/v2.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: clsx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:53:58 +00:00
dependabot[bot]
086dbe0d16 chore(deps-dev): bump c8 from 8.0.1 to 9.0.0 (#9878)
Bumps [c8](https://github.com/bcoe/c8) from 8.0.1 to 9.0.0.
- [Release notes](https://github.com/bcoe/c8/releases)
- [Changelog](https://github.com/bcoe/c8/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bcoe/c8/compare/v8.0.1...v9.0.0)

---
updated-dependencies:
- dependency-name: c8
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:44:37 +00:00
chris48s
9493d00483 migrate examples to openApi part 35; affects [npm] (#9866)
* migrate some services from examples to openApi

* clarify docs on scoped/unscoped packages
2024-01-06 16:41:42 +00:00
chris48s
a388c6e698 migrate examples to openApi part 36; affects [myget nuget resharper chocolatey powershellgallery] (#9873)
* update resharper tests

* migrate some services from examples to openapi
2024-01-06 16:41:37 +00:00
dependabot[bot]
95e98d405f chore(deps-dev): bump node-mocks-http from 1.14.0 to 1.14.1 (#9875)
Bumps [node-mocks-http](https://github.com/eugef/node-mocks-http) from 1.14.0 to 1.14.1.
- [Release notes](https://github.com/eugef/node-mocks-http/releases)
- [Changelog](https://github.com/eugef/node-mocks-http/blob/master/HISTORY.md)
- [Commits](https://github.com/eugef/node-mocks-http/compare/v.1.14.0...v1.14.1)

---
updated-dependencies:
- dependency-name: node-mocks-http
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:40:09 +00:00
dependabot[bot]
95cc63bafa chore(deps): bump @sentry/node from 7.91.0 to 7.92.0 (#9879)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.91.0 to 7.92.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.91.0...7.92.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:34:40 +00:00
dependabot[bot]
4e01b1f399 chore(deps-dev): bump eslint-plugin-jsdoc from 46.9.1 to 48.0.2 (#9874)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 46.9.1 to 48.0.2.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Changelog](https://github.com/gajus/eslint-plugin-jsdoc/blob/main/.releaserc)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v46.9.1...v48.0.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:34:03 +00:00
dependabot[bot]
2ba3c70c86 chore(deps-dev): bump @typescript-eslint/parser from 6.16.0 to 6.17.0 (#9877)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 6.16.0 to 6.17.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.17.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:33:21 +00:00
dependabot[bot]
6966a13144 chore(deps-dev): bump tsd from 0.30.1 to 0.30.3 (#9876)
Bumps [tsd](https://github.com/tsdjs/tsd) from 0.30.1 to 0.30.3.
- [Release notes](https://github.com/tsdjs/tsd/releases)
- [Commits](https://github.com/tsdjs/tsd/compare/v0.30.1...v0.30.3)

---
updated-dependencies:
- dependency-name: tsd
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-06 16:32:28 +00:00
GuillaumeG
34ef876be5 Add [galaxytoolshed] Version (#8249)
* feat(services): galaxytoolshed, add version and update base schema

* fix(services): galaxytoolshed, update schema

* fix(services): galaxytoolshed, preview updated

* fix(services): galaxytoolshed, enable openApi for version

* fix(services): galaxytoolshed, version labels
2024-01-05 17:36:51 +00:00
chris48s
26ae5a0cf4 fix default style docs for social badges (#9869)
* fix default style docs for social badges

* Update core/base-service/openapi.js

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>

* update test

---------

Co-authored-by: Pierre-Yves Bigourdan <10694593+PyvesB@users.noreply.github.com>
2024-01-04 20:23:27 +00:00
chris48s
e8a148eed3 migrate examples to openApi part 34; affects [github] (#9865)
* fix github service tests

* migrate some services from examples to openApi

* document latest variant with separate examples

making seperate routes for the /{version} and /latest variants
allows us to only show the docs for
- include_prereleases
- sort and
- filter
in the situation where they are relevant
2024-01-04 20:02:03 +00:00
Aliaksei Hrynko
b1f5aecf36 Fix typo in OpenAPI service documentation (#9868) 2024-01-02 18:00:28 +00:00
Pierre-Yves Bigourdan
89f5acfc9d migrate examples to openApi part 33; affects [bower drone localizely mozillaobservatory pythonversionfromtoml] (#9864)
* migrate some services from examples to openApi

* Fix Drone examples and tests

* Separate Mozilla Observatory publish documentation
2024-01-01 17:38:03 +00:00
chris48s
a7f2396202 migrate some services from examples to openApi part 31; affects [packagecontrol discourse] (#9858)
* migrate some services from examples to openApi

* update e2e test assertion
2024-01-01 10:32:38 +00:00
chris48s
09c83d9d46 migrate some services from examples to openApi (#9857) 2024-01-01 10:32:36 +00:00
109 changed files with 2774 additions and 2348 deletions

View File

@@ -10,4 +10,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@v4
- name: 'Dependency Review'
uses: actions/dependency-review-action@v3
uses: actions/dependency-review-action@v4

View File

@@ -16,7 +16,7 @@ jobs:
- name: Cache Cypress binary
id: cache-cypress
uses: actions/cache@v3
uses: actions/cache@v4
env:
cache-name: cache-cypress
with:

View File

@@ -4,6 +4,19 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2024-02-01
- feat: added up_message and down_message to [uptimerobotstatus] [#9662](https://github.com/badges/shields/issues/9662)
- Add [Hangar] Badges [#9800](https://github.com/badges/shields/issues/9800)
- sort categories by title (except core) [#9888](https://github.com/badges/shields/issues/9888)
- Add Support for [Nostr] Followers [#9870](https://github.com/badges/shields/issues/9870)
- [thunderstore] replace experimental API usage with newly available v1 API [#9886](https://github.com/badges/shields/issues/9886)
- Update [Gitea] defaults to gitea.com [#9872](https://github.com/badges/shields/issues/9872)
- [crates] MSRV Badge [#9871](https://github.com/badges/shields/issues/9871)
- Add [galaxytoolshed] Version [#8249](https://github.com/badges/shields/issues/8249)
- fix default style docs for social badges [#9869](https://github.com/badges/shields/issues/9869)
- Dependency updates
## server-2024-01-01
The most important changes in this release for users hosting their own instance are:

View File

@@ -109,7 +109,7 @@ When server source files change, the badge server should automatically restart
itself (using [nodemon][]). When the frontend files change, the frontend dev
server (`docusaurus start`) should also automatically reload. However the badge
definitions are built only before the server first starts. To regenerate those,
either run `npm run defs` or manually restart the server.
either run `npm run prestart` or manually restart the server.
To debug a badge from the command line, run `npm run badge -- /npm/v/nock`.
It also works with full URLs like

View File

@@ -198,7 +198,13 @@ function addGlobalProperties(endpoints) {
return paths
}
function services2openapi(services) {
function sortPaths(obj) {
const entries = Object.entries(obj)
entries.sort((a, b) => a[1].get.summary.localeCompare(b[1].get.summary))
return Object.fromEntries(entries)
}
function services2openapi(services, sort) {
const paths = {}
for (const service of services) {
if (service.openApi) {
@@ -221,10 +227,10 @@ function services2openapi(services) {
}
}
}
return paths
return sort ? sortPaths(paths) : paths
}
function category2openapi(category, services) {
function category2openapi({ category, services, sort = false }) {
const spec = {
openapi: '3.0.0',
info: {
@@ -241,7 +247,9 @@ function category2openapi(category, services) {
name: 'style',
in: 'query',
required: false,
description: 'If not specified, the defautl style is "flat".',
description: `If not specified, the default style for this badge is "${
category.name.toLowerCase() === 'social' ? 'social' : 'flat'
}".`,
schema: {
type: 'string',
enum: ['flat', 'flat-square', 'plastic', 'for-the-badge', 'social'],
@@ -332,7 +340,7 @@ function category2openapi(category, services) {
},
},
},
paths: services2openapi(services),
paths: services2openapi(services, sort),
}
return spec

View File

@@ -88,7 +88,8 @@ const expected = {
name: 'style',
in: 'query',
required: false,
description: 'If not specified, the defautl style is "flat".',
description:
'If not specified, the default style for this badge is "flat".',
schema: {
enum: ['flat', 'flat-square', 'plastic', 'for-the-badge', 'social'],
type: 'string',
@@ -376,10 +377,13 @@ describe('category2openapi', function () {
it('generates an Open API spec', function () {
expect(
clean(
category2openapi({ name: 'build' }, [
OpenApiService.getDefinition(),
LegacyService.getDefinition(),
]),
category2openapi({
category: { name: 'build' },
services: [
OpenApiService.getDefinition(),
LegacyService.getDefinition(),
],
}),
),
).to.deep.equal(expected)
})

View File

@@ -10,6 +10,7 @@ import {
import { isValidCategory } from './categories.js'
import { MetricHelper } from './metric-helper.js'
import { isValidRoute, prepareRoute, namedParamsForMatch } from './route.js'
import { openApiSchema } from './service-definitions.js'
import trace from './trace.js'
const attrSchema = Joi.object({
@@ -18,6 +19,7 @@ const attrSchema = Joi.object({
isDeprecated: Joi.boolean().default(true),
route: isValidRoute,
examples: Joi.array().has(Joi.object()).default([]),
openApi: openApiSchema,
transformPath: Joi.func()
.maxArity(1)
.required()
@@ -37,6 +39,7 @@ export default function redirector(attrs) {
isDeprecated,
route,
examples,
openApi,
transformPath,
transformQueryParams,
overrideTransformedQueryParams,
@@ -53,6 +56,7 @@ export default function redirector(attrs) {
static isDeprecated = isDeprecated
static route = route
static examples = examples
static openApi = openApi
static register({ camp, metricInstance }, { rasterUrl }) {
const { regex, captureNames } = prepareRoute({

View File

@@ -6,6 +6,33 @@ const objectOfKeyValues = Joi.object()
.pattern(/./, Joi.string().allow(null))
.required()
const openApiSchema = Joi.object().pattern(
/./,
Joi.object({
get: Joi.object({
summary: Joi.string().required(),
description: Joi.string(),
parameters: Joi.array()
.items(
Joi.object({
name: Joi.string().required(),
description: Joi.string(),
in: Joi.string().valid('query', 'path').required(),
required: Joi.boolean().required(),
schema: Joi.object({
type: Joi.string().required(),
enum: Joi.array(),
}).required(),
allowEmptyValue: Joi.boolean(),
example: Joi.string().allow(null),
}),
)
.min(1)
.required(),
}).required(),
}).required(),
)
const serviceDefinition = Joi.object({
category: Joi.string().required(),
name: Joi.string().required(),
@@ -43,32 +70,7 @@ const serviceDefinition = Joi.object({
}),
)
.default([]),
openApi: Joi.object().pattern(
/./,
Joi.object({
get: Joi.object({
summary: Joi.string().required(),
description: Joi.string(),
parameters: Joi.array()
.items(
Joi.object({
name: Joi.string().required(),
description: Joi.string(),
in: Joi.string().valid('query', 'path').required(),
required: Joi.boolean().required(),
schema: Joi.object({
type: Joi.string().required(),
enum: Joi.array(),
}).required(),
allowEmptyValue: Joi.boolean(),
example: Joi.string().allow(null),
}),
)
.min(1)
.required(),
}).required(),
}).required(),
),
openApi: openApiSchema,
}).required()
function assertValidServiceDefinition(service, message = undefined) {
@@ -93,4 +95,8 @@ function assertValidServiceDefinitionExport(examples, message = undefined) {
Joi.assert(examples, serviceDefinitionExport, message)
}
export { assertValidServiceDefinition, assertValidServiceDefinitionExport }
export {
assertValidServiceDefinition,
assertValidServiceDefinitionExport,
openApiSchema,
}

View File

@@ -25,7 +25,7 @@ describe('Frontend', function () {
cy.contains('Build')
cy.contains('Chat').click()
cy.contains('Discourse status')
cy.contains('Discourse Status')
cy.contains('Stack Exchange questions')
})

View File

@@ -0,0 +1,21 @@
---
slug: simple-icons-11
title: Simple Icons 11
authors:
name: chris48s
title: Shields.io Core Team
url: https://github.com/chris48s
image_url: https://avatars.githubusercontent.com/u/6025893
tags: []
---
Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 11. This release removes the following 4 icons:
- Babylon.js
- Hulu
- Pepsi
- Uno
More details can be found in the [release notes](https://github.com/simple-icons/simple-icons/releases/tag/11.0.0).
Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the [SimpleIcons](https://github.com/simple-icons/simple-icons) project.

393
package-lock.json generated
View File

@@ -11,7 +11,7 @@
"dependencies": {
"@renovatebot/pep440": "^3.0.17",
"@renovatebot/ruby-semver": "^3.0.22",
"@sentry/node": "^7.91.0",
"@sentry/node": "^7.98.0",
"@shields_io/camp": "^18.1.2",
"@xmldom/xmldom": "0.8.10",
"badge-maker": "file:badge-maker",
@@ -20,19 +20,19 @@
"chalk": "^5.3.0",
"check-node-version": "^4.2.1",
"cloudflare-middleware": "^1.0.4",
"config": "^3.3.9",
"config": "^3.3.10",
"cross-env": "^7.0.3",
"dayjs": "^1.11.10",
"decamelize": "^3.2.0",
"emojic": "^1.1.17",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^4.3.2",
"fast-xml-parser": "^4.3.3",
"glob": "^10.3.10",
"global-agent": "^3.0.0",
"got": "^14.0.0",
"graphql": "16.8.1",
"graphql-tag": "^2.12.6",
"joi": "17.11.0",
"joi": "17.12.0",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
"jsonpath": "~1.1.1",
@@ -51,8 +51,8 @@
"qs": "^6.11.2",
"query-string": "^8.1.0",
"semver": "~7.5.4",
"simple-icons": "10.4.0",
"smol-toml": "1.1.3",
"simple-icons": "11.2.0",
"smol-toml": "1.1.4",
"webextension-store-meta": "^1.0.5",
"xpath": "~0.0.34"
},
@@ -60,17 +60,17 @@
"@docusaurus/core": "^2.4.3",
"@easyops-cn/docusaurus-search-local": "^0.40.1",
"@mdx-js/react": "^1.6.21",
"@typescript-eslint/parser": "^6.16.0",
"c8": "^8.0.1",
"@typescript-eslint/parser": "^6.19.1",
"c8": "^9.1.0",
"caller": "^1.1.0",
"chai": "^4.3.10",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
"chai-datetime": "^1.8.0",
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clsx": "^2.0.0",
"clsx": "^2.1.0",
"concurrently": "^8.2.2",
"cypress": "^13.6.2",
"cypress": "^13.6.3",
"cypress-wait-for-stable-dom": "^0.1.0",
"danger": "^11.3.1",
"deepmerge": "^4.3.1",
@@ -80,11 +80,11 @@
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-config-standard-react": "13.0.0",
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-icedfrisby": "^0.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsdoc": "^46.9.1",
"eslint-plugin-jsdoc": "^48.0.4",
"eslint-plugin-mocha": "^10.2.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
@@ -104,13 +104,13 @@
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.2.1",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.4.0",
"node-mocks-http": "^1.14.0",
"nodemon": "^3.0.2",
"nock": "13.5.0",
"node-mocks-http": "^1.14.1",
"nodemon": "^3.0.3",
"npm-run-all": "^4.1.5",
"open-cli": "^8.0.0",
"portfinder": "^1.0.32",
"prettier": "3.1.1",
"prettier": "3.2.4",
"prism-react-renderer": "^2.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -122,7 +122,7 @@
"sinon-chai": "^3.7.0",
"snap-shot-it": "^7.9.10",
"start-server-and-test": "2.0.3",
"tsd": "^0.30.1",
"tsd": "^0.30.4",
"url": "^0.11.3"
},
"engines": {
@@ -4678,59 +4678,58 @@
}
},
"node_modules/@sentry-internal/tracing": {
"version": "7.91.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.91.0.tgz",
"integrity": "sha512-JH5y6gs6BS0its7WF2DhySu7nkhPDfZcdpAXldxzIlJpqFkuwQKLU5nkYJpiIyZz1NHYYtW5aum2bV2oCOdDRA==",
"version": "7.98.0",
"resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.98.0.tgz",
"integrity": "sha512-FnhD2uMLIAJvv4XsYPv3qsTTtxrImyLxiZacudJyaWFhxoeVQ8bKKbWJ/Ar68FAwqTtjXMeY5evnEBbRMcQlaA==",
"dependencies": {
"@sentry/core": "7.91.0",
"@sentry/types": "7.91.0",
"@sentry/utils": "7.91.0"
"@sentry/core": "7.98.0",
"@sentry/types": "7.98.0",
"@sentry/utils": "7.98.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/core": {
"version": "7.91.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.91.0.tgz",
"integrity": "sha512-tu+gYq4JrTdrR+YSh5IVHF0fJi/Pi9y0HZ5H9HnYy+UMcXIotxf6hIEaC6ZKGeLWkGXffz2gKpQLe/g6vy/lPA==",
"version": "7.98.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.98.0.tgz",
"integrity": "sha512-baRUcpCNGyk7cApQHMfqEZJkXdvAKK+z/dVWiMqWc5T5uhzMnPE8/gjP1JZsMtJSQ8g5nHimBdI5TwOyZtxPaA==",
"dependencies": {
"@sentry/types": "7.91.0",
"@sentry/utils": "7.91.0"
"@sentry/types": "7.98.0",
"@sentry/utils": "7.98.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/node": {
"version": "7.91.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.91.0.tgz",
"integrity": "sha512-hTIfSQxD7L+AKIqyjoq8CWBRkEQrrMZmA3GSZgPI5JFWBHgO0HBo5TH/8TU81oEJh6kqqHAl2ObMhmcnaFqlzg==",
"version": "7.98.0",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.98.0.tgz",
"integrity": "sha512-9cHW217DnU9wC4iR+QxmY3q59N1Touh23hPMDtpMRmbRHSgrmLMoHTVPhK9zHsXRs0mUeidmMqY1ubAWauQByw==",
"dependencies": {
"@sentry-internal/tracing": "7.91.0",
"@sentry/core": "7.91.0",
"@sentry/types": "7.91.0",
"@sentry/utils": "7.91.0",
"https-proxy-agent": "^5.0.0"
"@sentry-internal/tracing": "7.98.0",
"@sentry/core": "7.98.0",
"@sentry/types": "7.98.0",
"@sentry/utils": "7.98.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/types": {
"version": "7.91.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.91.0.tgz",
"integrity": "sha512-bcQnb7J3P3equbCUc+sPuHog2Y47yGD2sCkzmnZBjvBT0Z1B4f36fI/5WjyZhTjLSiOdg3F2otwvikbMjmBDew==",
"version": "7.98.0",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.98.0.tgz",
"integrity": "sha512-pc034ziM0VTETue4bfBcBqTWGy4w0okidtoZJjGVrYAfE95ObZnUGVj/XYIQ3FeCYWIa7NFN2MvdsCS0buwivQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/@sentry/utils": {
"version": "7.91.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.91.0.tgz",
"integrity": "sha512-fvxjrEbk6T6Otu++Ax9ntlQ0sGRiwSC179w68aC3u26Wr30FAIRKqHTCCdc2jyWk7Gd9uWRT/cq+g8NG/8BfSg==",
"version": "7.98.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.98.0.tgz",
"integrity": "sha512-0/LY+kpHxItVRY0xPDXPXVsKRb95cXsGSQf8sVMtfSjz++0bLL1U4k7PFz1c5s2/Vk0B8hS6duRrgMv6dMIZDw==",
"dependencies": {
"@sentry/types": "7.91.0"
"@sentry/types": "7.98.0"
},
"engines": {
"node": ">=8"
@@ -5237,9 +5236,9 @@
"dev": true
},
"node_modules/@types/express": {
"version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
"integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
@@ -5626,15 +5625,15 @@
}
},
"node_modules/@typescript-eslint/parser": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.16.0.tgz",
"integrity": "sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==",
"version": "6.19.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.1.tgz",
"integrity": "sha512-WEfX22ziAh6pRE9jnbkkLGp/4RhTpffr2ZK5bJ18M8mIfA8A+k97U9ZyaXCEJRlmMHh7R9MJZWXp/r73DzINVQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "6.16.0",
"@typescript-eslint/types": "6.16.0",
"@typescript-eslint/typescript-estree": "6.16.0",
"@typescript-eslint/visitor-keys": "6.16.0",
"@typescript-eslint/scope-manager": "6.19.1",
"@typescript-eslint/types": "6.19.1",
"@typescript-eslint/typescript-estree": "6.19.1",
"@typescript-eslint/visitor-keys": "6.19.1",
"debug": "^4.3.4"
},
"engines": {
@@ -5654,13 +5653,13 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz",
"integrity": "sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==",
"version": "6.19.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.1.tgz",
"integrity": "sha512-4CdXYjKf6/6aKNMSly/BP4iCSOpvMmqtDzRtqFyyAae3z5kkqEjKndR5vDHL8rSuMIIWP8u4Mw4VxLyxZW6D5w==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.16.0",
"@typescript-eslint/visitor-keys": "6.16.0"
"@typescript-eslint/types": "6.19.1",
"@typescript-eslint/visitor-keys": "6.19.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -5671,9 +5670,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.16.0.tgz",
"integrity": "sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==",
"version": "6.19.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.1.tgz",
"integrity": "sha512-6+bk6FEtBhvfYvpHsDgAL3uo4BfvnTnoge5LrrCj2eJN8g3IJdLTD4B/jK3Q6vo4Ql/Hoip9I8aB6fF+6RfDqg==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
@@ -5684,13 +5683,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz",
"integrity": "sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==",
"version": "6.19.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.1.tgz",
"integrity": "sha512-aFdAxuhzBFRWhy+H20nYu19+Km+gFfwNO4TEqyszkMcgBDYQjmPJ61erHxuT2ESJXhlhrO7I5EFIlZ+qGR8oVA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.16.0",
"@typescript-eslint/visitor-keys": "6.16.0",
"@typescript-eslint/types": "6.19.1",
"@typescript-eslint/visitor-keys": "6.19.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -5736,12 +5735,12 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.16.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz",
"integrity": "sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==",
"version": "6.19.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.1.tgz",
"integrity": "sha512-gkdtIO+xSO/SmI0W68DBg4u1KElmIUo3vXzgHyGPs6cxgB0sa3TlptRAAE0hUY1hM6FcDKEv7aIwiTGm76cXfQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.16.0",
"@typescript-eslint/types": "6.19.1",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
@@ -5990,6 +5989,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"dev": true,
"dependencies": {
"debug": "4"
},
@@ -7193,19 +7193,18 @@
}
},
"node_modules/c8": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz",
"integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==",
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
"integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==",
"dev": true,
"dependencies": {
"@bcoe/v8-coverage": "^0.2.3",
"@istanbuljs/schema": "^0.1.3",
"find-up": "^5.0.0",
"foreground-child": "^2.0.0",
"foreground-child": "^3.1.1",
"istanbul-lib-coverage": "^3.2.0",
"istanbul-lib-report": "^3.0.1",
"istanbul-reports": "^3.1.6",
"rimraf": "^3.0.2",
"test-exclude": "^6.0.0",
"v8-to-istanbul": "^9.0.0",
"yargs": "^17.7.2",
@@ -7215,7 +7214,7 @@
"c8": "bin/c8.js"
},
"engines": {
"node": ">=12"
"node": ">=14.14.0"
}
},
"node_modules/c8/node_modules/ansi-styles": {
@@ -7271,26 +7270,6 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true
},
"node_modules/c8/node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/c8/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
@@ -7300,21 +7279,6 @@
"node": ">=8"
}
},
"node_modules/c8/node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/c8/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -7545,9 +7509,9 @@
}
},
"node_modules/chai": {
"version": "4.3.10",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
"integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==",
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
"integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==",
"dependencies": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.3",
@@ -8172,9 +8136,9 @@
}
},
"node_modules/clsx": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
"integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==",
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz",
"integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==",
"dev": true,
"engines": {
"node": ">=6"
@@ -8547,9 +8511,9 @@
}
},
"node_modules/config": {
"version": "3.3.9",
"resolved": "https://registry.npmjs.org/config/-/config-3.3.9.tgz",
"integrity": "sha512-G17nfe+cY7kR0wVpc49NCYvNtelm/pPy8czHoFkAgtV1lkmcp7DHtWCdDu+C9Z7gb2WVqa9Tm3uF9aKaPbCfhg==",
"version": "3.3.10",
"resolved": "https://registry.npmjs.org/config/-/config-3.3.10.tgz",
"integrity": "sha512-9Kl3LpQ6zj93KaqgfIMTcpwTpgozFOqNl/Dk7mjras1BgGIOlqxWkyIGeU1my+sRuskRYwrCATgCk1RjAnRPGA==",
"dependencies": {
"json5": "^2.2.3"
},
@@ -9305,15 +9269,14 @@
"dev": true
},
"node_modules/cypress": {
"version": "13.6.2",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.2.tgz",
"integrity": "sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==",
"version": "13.6.3",
"resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.3.tgz",
"integrity": "sha512-d/pZvgwjAyZsoyJ3FOsJT5lDsqnxQ/clMqnNc++rkHjbkkiF2h9s0JsZSyyH4QXhVFW3zPFg82jD25roFLOdZA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
"@cypress/request": "^3.0.0",
"@cypress/xvfb": "^1.2.4",
"@types/node": "^18.17.5",
"@types/sinonjs__fake-timers": "8.1.1",
"@types/sizzle": "^2.3.2",
"arch": "^2.2.0",
@@ -11335,9 +11298,9 @@
}
},
"node_modules/eslint-plugin-chai-friendly": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.2.tgz",
"integrity": "sha512-LOIfGx5sZZ5FwM1shr2GlYAWV9Omdi+1/3byuVagvQNoGUuU0iHhp7AfjA1uR+4dJ4Isfb4+FwBJgQajIw9iAg==",
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.7.4.tgz",
"integrity": "sha512-PGPjJ8diYgX1mjLxGJqRop2rrGwZRKImoEOwUOgoIhg0p80MkTaqvmFLe5TF7/iagZHggasvIfQlUyHIhK/PYg==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -11501,9 +11464,9 @@
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "46.9.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.9.1.tgz",
"integrity": "sha512-11Ox5LCl2wY7gGkp9UOyew70o9qvii1daAH+h/MFobRVRNcy7sVlH+jm0HQdgcvcru6285GvpjpUyoa051j03Q==",
"version": "48.0.4",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.0.4.tgz",
"integrity": "sha512-A0cH+5svWPXzGZszBjXA1t0aAqVGS+/x3i02KFmb73rU0iMLnadEcVWcD/dGBZHIfAMKr3YpWh58f6wn4N909w==",
"dev": true,
"dependencies": {
"@es-joy/jsdoccomment": "~0.41.0",
@@ -11517,10 +11480,10 @@
"spdx-expression-parse": "^4.0.0"
},
"engines": {
"node": ">=16"
"node": ">=18"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": {
@@ -12385,9 +12348,9 @@
"dev": true
},
"node_modules/fast-xml-parser": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz",
"integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz",
"integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==",
"funding": [
{
"type": "github",
@@ -12758,9 +12721,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@@ -12787,23 +12750,24 @@
}
},
"node_modules/foreground-child": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz",
"integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
"dev": true,
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^3.0.2"
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=8.0.0"
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/foreground-child/node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dev": true,
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -12813,11 +12777,21 @@
"node": ">= 8"
}
},
"node_modules/foreground-child/node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/foreground-child/node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -13349,34 +13323,6 @@
"balanced-match": "^1.0.0"
}
},
"node_modules/glob/node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/glob/node_modules/foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^4.0.1"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/minimatch": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
@@ -13391,31 +13337,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/signal-exit": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz",
"integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==",
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/glob/node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/global-agent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
@@ -14329,6 +14250,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"dev": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -15651,13 +15573,13 @@
}
},
"node_modules/joi": {
"version": "17.11.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz",
"integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==",
"version": "17.12.0",
"resolved": "https://registry.npmjs.org/joi/-/joi-17.12.0.tgz",
"integrity": "sha512-HSLsmSmXz+PV9PYoi3p7cgIbj06WnEBNT28n+bbBNcPZXZFqCzzvGqpTBPujx/Z0nh1+KNQPDrNgdmQ8dq0qYw==",
"dependencies": {
"@hapi/hoek": "^9.0.0",
"@hapi/topo": "^5.0.0",
"@sideway/address": "^4.1.3",
"@hapi/hoek": "^9.3.0",
"@hapi/topo": "^5.1.0",
"@sideway/address": "^4.1.4",
"@sideway/formula": "^3.0.1",
"@sideway/pinpoint": "^2.0.0"
}
@@ -18199,9 +18121,9 @@
}
},
"node_modules/nock": {
"version": "13.4.0",
"resolved": "https://registry.npmjs.org/nock/-/nock-13.4.0.tgz",
"integrity": "sha512-W8NVHjO/LCTNA64yxAPHV/K47LpGYcVzgKd3Q0n6owhwvD0Dgoterc25R4rnZbckJEb6Loxz1f5QMuJpJnbSyQ==",
"version": "13.5.0",
"resolved": "https://registry.npmjs.org/nock/-/nock-13.5.0.tgz",
"integrity": "sha512-9hc1eCS2HtOz+sE9W7JQw/tXJktg0zoPSu48s/pYe73e25JW9ywiowbqnUSd7iZPeVawLcVpPZeZS312fwSY+g==",
"dev": true,
"dependencies": {
"debug": "^4.1.0",
@@ -18279,11 +18201,13 @@
}
},
"node_modules/node-mocks-http": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.14.0.tgz",
"integrity": "sha512-yOb3zNr9/5N27TAWxxMlrvWp4nNfRcrC0dAiqyj5NLyk6Jm80T95AhNj/YjThelkTkmTutS6RAB8pkmpkMofdA==",
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/node-mocks-http/-/node-mocks-http-1.14.1.tgz",
"integrity": "sha512-mfXuCGonz0A7uG1FEjnypjm34xegeN5+HI6xeGhYKecfgaZhjsmYoLE9LEFmT+53G1n8IuagPZmVnEL/xNsFaA==",
"dev": true,
"dependencies": {
"@types/express": "^4.17.21",
"@types/node": "^20.10.6",
"accepts": "^1.3.7",
"content-disposition": "^0.5.3",
"depd": "^1.1.0",
@@ -18299,6 +18223,15 @@
"node": ">=14"
}
},
"node_modules/node-mocks-http/node_modules/@types/node": {
"version": "20.10.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz",
"integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/node-pg-migrate": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.2.tgz",
@@ -18397,9 +18330,9 @@
}
},
"node_modules/nodemon": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz",
"integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
"integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
@@ -21270,9 +21203,9 @@
}
},
"node_modules/prettier": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
"integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==",
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz",
"integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==",
"dev": true,
"bin": {
"prettier": "bin/prettier.cjs"
@@ -24029,9 +23962,9 @@
}
},
"node_modules/simple-icons": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-10.4.0.tgz",
"integrity": "sha512-XBoU1ljCsWjw59IVkaQ1nKc0PiaDAAKNFVx59ueC0tBy4WY/I4Q040sGj6ok2cZRLT8zBzL1HaTubi8MRqmojQ==",
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-11.2.0.tgz",
"integrity": "sha512-ZT/+2+pSg7kGaWBorPxaktwb3pK18R945XnPU895qPzGDN1LxkHfDbejbBnAvIElYYPgUPstqUIjyPSvZPSfTg==",
"engines": {
"node": ">=0.12.18"
},
@@ -24184,9 +24117,9 @@
}
},
"node_modules/smol-toml": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.1.3.tgz",
"integrity": "sha512-qTyy6Owjho1ISBmxj4HdrFWB2kMQ5RczU6J04OqslSfdSH656OIHuomHS4ZDvhwm37nig/uXyiTMJxlC9zIVfw==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.1.4.tgz",
"integrity": "sha512-Y0OT8HezWsTNeEOSVxDnKOW/AyNXHQ4BwJNbAXlLTF5wWsBvrcHhIkE5Rf8kQMLmgf7nDX3PVOlgC6/Aiggu3Q==",
"engines": {
"node": ">= 18",
"pnpm": ">= 8"
@@ -25785,9 +25718,9 @@
}
},
"node_modules/tsd": {
"version": "0.30.1",
"resolved": "https://registry.npmjs.org/tsd/-/tsd-0.30.1.tgz",
"integrity": "sha512-OKyeWzTGuaidnYPcSCk/Jz6GY+3A4cNe0tLvJXT7V0vyQ8gy4ISNAetex6YodOkJw7b641B5vokmOZFeW/mpmQ==",
"version": "0.30.4",
"resolved": "https://registry.npmjs.org/tsd/-/tsd-0.30.4.tgz",
"integrity": "sha512-ncC4SwAeUk0OTcXt5h8l0/gOLHJSp9ogosvOADT6QYzrl0ITm398B3wkz8YESqefIsEEwvYAU8bvo7/rcN/M0Q==",
"dev": true,
"dependencies": {
"@tsd/typescript": "~5.3.3",
@@ -26305,6 +26238,12 @@
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
},
"node_modules/unherit": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz",

View File

@@ -23,7 +23,7 @@
"dependencies": {
"@renovatebot/pep440": "^3.0.17",
"@renovatebot/ruby-semver": "^3.0.22",
"@sentry/node": "^7.91.0",
"@sentry/node": "^7.98.0",
"@shields_io/camp": "^18.1.2",
"@xmldom/xmldom": "0.8.10",
"badge-maker": "file:badge-maker",
@@ -32,19 +32,19 @@
"chalk": "^5.3.0",
"check-node-version": "^4.2.1",
"cloudflare-middleware": "^1.0.4",
"config": "^3.3.9",
"config": "^3.3.10",
"cross-env": "^7.0.3",
"dayjs": "^1.11.10",
"decamelize": "^3.2.0",
"emojic": "^1.1.17",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^4.3.2",
"fast-xml-parser": "^4.3.3",
"glob": "^10.3.10",
"global-agent": "^3.0.0",
"got": "^14.0.0",
"graphql": "16.8.1",
"graphql-tag": "^2.12.6",
"joi": "17.11.0",
"joi": "17.12.0",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
"jsonpath": "~1.1.1",
@@ -63,8 +63,8 @@
"qs": "^6.11.2",
"query-string": "^8.1.0",
"semver": "~7.5.4",
"simple-icons": "10.4.0",
"smol-toml": "1.1.3",
"simple-icons": "11.2.0",
"smol-toml": "1.1.4",
"webextension-store-meta": "^1.0.5",
"xpath": "~0.0.34"
},
@@ -147,17 +147,17 @@
"@docusaurus/core": "^2.4.3",
"@easyops-cn/docusaurus-search-local": "^0.40.1",
"@mdx-js/react": "^1.6.21",
"@typescript-eslint/parser": "^6.16.0",
"c8": "^8.0.1",
"@typescript-eslint/parser": "^6.19.1",
"c8": "^9.1.0",
"caller": "^1.1.0",
"chai": "^4.3.10",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
"chai-datetime": "^1.8.0",
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clsx": "^2.0.0",
"clsx": "^2.1.0",
"concurrently": "^8.2.2",
"cypress": "^13.6.2",
"cypress": "^13.6.3",
"cypress-wait-for-stable-dom": "^0.1.0",
"danger": "^11.3.1",
"deepmerge": "^4.3.1",
@@ -167,11 +167,11 @@
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-config-standard-react": "13.0.0",
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-icedfrisby": "^0.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jsdoc": "^46.9.1",
"eslint-plugin-jsdoc": "^48.0.4",
"eslint-plugin-mocha": "^10.2.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
@@ -191,13 +191,13 @@
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.2.1",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.4.0",
"node-mocks-http": "^1.14.0",
"nodemon": "^3.0.2",
"nock": "13.5.0",
"node-mocks-http": "^1.14.1",
"nodemon": "^3.0.3",
"npm-run-all": "^4.1.5",
"open-cli": "^8.0.0",
"portfinder": "^1.0.32",
"prettier": "3.1.1",
"prettier": "3.2.4",
"prism-react-renderer": "^2.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -209,7 +209,7 @@
"sinon-chai": "^3.7.0",
"snap-shot-it": "^7.9.10",
"start-server-and-test": "2.0.3",
"tsd": "^0.30.1",
"tsd": "^0.30.4",
"url": "^0.11.3"
},
"engines": {

View File

@@ -27,7 +27,7 @@ function writeSpec(filename, spec) {
writeSpec(
path.join(specsPath, `${category.id}.yaml`),
category2openapi(category, services),
category2openapi({ category, services, sort: true }),
)
}
@@ -44,6 +44,10 @@ function writeSpec(filename, spec) {
)
writeSpec(
path.join(specsPath, '1core.yaml'),
category2openapi({ name: 'Core' }, coreServices),
category2openapi({
category: { name: 'Core' },
services: coreServices,
sort: false,
}),
)
})()

View File

@@ -1,18 +1,22 @@
import { renderVersionBadge } from '../version.js'
import { InvalidResponse } from '../index.js'
import { InvalidResponse, pathParams } from '../index.js'
import BaseBowerService from './bower-base.js'
export default class BowerVersion extends BaseBowerService {
static category = 'version'
static route = { base: 'bower/v', pattern: ':packageName' }
static examples = [
{
title: 'Bower Version',
namedParams: { packageName: 'bootstrap' },
staticPreview: renderVersionBadge({ version: '4.2.1' }),
static openApi = {
'/bower/v/{packageName}': {
get: {
summary: 'Bower Version',
parameters: pathParams({
name: 'packageName',
example: 'bootstrap',
}),
},
},
]
}
static defaultBadgeData = { label: 'bower' }

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import { coveragePercentage } from '../color-formatters.js'
import { BaseSvgScrapingService } from '../index.js'
import { BaseSvgScrapingService, pathParam, queryParam } from '../index.js'
import { parseJson } from '../../core/base-service/json.js'
// https://docs.codecov.io/reference#totals
@@ -35,12 +35,12 @@ const svgValueMatcher = />(\d{1,3}%|unknown)<\/text><\/g>/
const badgeTokenPattern = /^\w{10}$/
const documentation = `
const description = `
<p>
You may specify a Codecov badge token to get coverage for a private repository.
</p>
<p>
You can find the token under the badge section of your project settings page, in this url: <code>https://codecov.io/{vcsName}/{user}/{repo}/settings/badge</code>.
You can find the token under the badge section of your project settings page, in this url: <code>https://codecov.io/&#60;vcsName&#62;/&#60;user&#62;/&#60;repo&#62;/settings/badge</code>.
</p>
`
@@ -54,39 +54,43 @@ export default class Codecov extends BaseSvgScrapingService {
queryParamSchema,
}
static examples = [
{
title: 'Codecov',
pattern: ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo',
namedParams: {
vcsName: 'github',
user: 'codecov',
repo: 'example-node',
static openApi = {
'/codecov/c/{vcsName}/{user}/{repo}': {
get: {
summary: 'Codecov',
description,
parameters: [
pathParam({
name: 'vcsName',
example: 'github',
schema: { type: 'string', enum: this.getEnum('vcsName') },
}),
pathParam({ name: 'user', example: 'codecov' }),
pathParam({ name: 'repo', example: 'example-node' }),
queryParam({ name: 'token', example: 'a1b2c3d4e5' }),
queryParam({ name: 'flag', example: 'flag_name' }),
],
},
queryParams: {
token: 'a1b2c3d4e5',
flag: 'flag_name',
},
staticPreview: this.render({ coverage: 90 }),
documentation,
},
{
title: 'Codecov branch',
pattern: ':vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch',
namedParams: {
vcsName: 'github',
user: 'codecov',
repo: 'example-node',
branch: 'master',
'/codecov/c/{vcsName}/{user}/{repo}/{branch}': {
get: {
summary: 'Codecov (with branch)',
description,
parameters: [
pathParam({
name: 'vcsName',
example: 'github',
schema: { type: 'string', enum: this.getEnum('vcsName') },
}),
pathParam({ name: 'user', example: 'codecov' }),
pathParam({ name: 'repo', example: 'example-node' }),
pathParam({ name: 'branch', example: 'master' }),
queryParam({ name: 'token', example: 'a1b2c3d4e5' }),
queryParam({ name: 'flag', example: 'flag_name' }),
],
},
queryParams: {
token: 'a1b2c3d4e5',
flag: 'flag_name',
},
staticPreview: this.render({ coverage: 90 }),
documentation,
},
]
}
static defaultBadgeData = { label: 'coverage' }

View File

@@ -13,6 +13,7 @@ const crateSchema = Joi.object({
Joi.object({
downloads: nonNegativeInteger,
license: Joi.string().required().allow(null),
rust_version: Joi.string().allow(null),
}),
)
.min(1)
@@ -24,17 +25,11 @@ const versionSchema = Joi.object({
downloads: nonNegativeInteger,
num: Joi.string().required(),
license: Joi.string().required().allow(null),
rust_version: Joi.string().allow(null),
}).required(),
}).required()
const errorSchema = Joi.object({
errors: Joi.array()
.items(Joi.object({ detail: Joi.string().required() }))
.min(1)
.required(),
}).required()
const schema = Joi.alternatives(crateSchema, versionSchema, errorSchema)
const schema = Joi.alternatives(crateSchema, versionSchema)
class BaseCratesService extends BaseJsonService {
static defaultBadgeData = { label: 'crates.io' }

View File

@@ -1,5 +1,5 @@
import { renderDownloadsBadge } from '../downloads.js'
import { InvalidParameter, NotFound, pathParams } from '../index.js'
import { InvalidParameter, pathParams } from '../index.js'
import { BaseCratesService, description } from './crates-base.js'
export default class CratesDownloads extends BaseCratesService {
@@ -93,15 +93,6 @@ export default class CratesDownloads extends BaseCratesService {
const json = await this.fetch({ crate, version })
if (json.errors) {
/* a call like
https://crates.io/api/v1/crates/libc/0.1
or
https://crates.io/api/v1/crates/libc/0.1.76
returns a 200 OK with an errors object */
throw new NotFound({ prettyMessage: json.errors[0].detail })
}
const downloads = this.transform({ variant, json })
return this.constructor.render({ variant, downloads, version })

View File

@@ -55,7 +55,7 @@ t.create('recent downloads (with version)')
t.create('downloads (invalid version)')
.get('/d/libc/7.json')
.expectBadge({ label: 'crates.io', message: 'invalid semver: 7' })
.expectBadge({ label: 'crates.io', message: 'not found' })
t.create('downloads (not found)')
.get('/d/not-a-real-package.json')

View File

@@ -36,17 +36,7 @@ export default class CratesLicense extends BaseCratesService {
static defaultBadgeData = { label: 'license', color: 'blue' }
static render({ license: message }) {
return { message }
}
static transform({ errors, version, versions }) {
// crates.io returns a 200 response with an errors object in
// error scenarios, e.g. https://crates.io/api/v1/crates/libc/0.1
if (errors) {
throw new InvalidResponse({ prettyMessage: errors[0].detail })
}
static transform({ version, versions }) {
const license = version ? version.license : versions[0].license
if (!license) {
throw new InvalidResponse({ prettyMessage: 'invalid null license' })
@@ -58,6 +48,6 @@ export default class CratesLicense extends BaseCratesService {
async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
const { license } = this.constructor.transform(json)
return this.constructor.render({ license })
return { message: license }
}
}

View File

@@ -14,14 +14,6 @@ describe('CratesLicense', function () {
}).expect({ license: 'MIT/Apache 2.0' })
})
it('throws InvalidResponse on error response', function () {
expect(() =>
CratesLicense.transform({ errors: [{ detail: 'invalid semver' }] }),
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid semver')
})
it('throws InvalidResponse on null license with specific version', function () {
expect(() =>
CratesLicense.transform({ version: { num: '1.2.3', license: null } }),

View File

@@ -0,0 +1,68 @@
import { NotFound, pathParams } from '../index.js'
import {
BaseCratesService,
description as cratesIoDescription,
} from './crates-base.js'
const description = `
${cratesIoDescription}
MSRV is a crate's minimum suppported rust version,
the oldest version of Rust supported by the crate.
See the [Cargo Book](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
for more info.
`
export default class CratesMSRV extends BaseCratesService {
static category = 'platform-support'
static route = {
base: 'crates/msrv',
pattern: ':crate/:version?',
}
static openApi = {
'/crates/msrv/{crate}': {
get: {
summary: 'Crates.io MSRV',
description,
parameters: pathParams({
name: 'crate',
example: 'serde',
}),
},
},
'/crates/msrv/{crate}/{version}': {
get: {
summary: 'Crates.io MSRV (version)',
description,
parameters: pathParams(
{
name: 'crate',
example: 'serde',
},
{
name: 'version',
example: '1.0.194',
},
),
},
},
}
static defaultBadgeData = { label: 'msrv', color: 'blue' }
static transform({ version, versions }) {
const msrv = version ? version.rust_version : versions[0].rust_version
if (!msrv) {
throw new NotFound({ prettyMessage: 'unknown' })
}
return { msrv }
}
async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
const { msrv } = this.constructor.transform(json)
return { message: msrv }
}
}

View File

@@ -0,0 +1,15 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
// dummy crate i created specifically for this test case
t.create('msrv')
.get('/shields-test-dummy-crate-msrv-3452398210.json')
.expectBadge({ label: 'msrv', message: '1.69' })
t.create('msrv (with version)')
.get('/shields-test-dummy-crate-msrv-3452398210/0.69.0.json')
.expectBadge({ label: 'msrv', message: '1.69' })
t.create('msrv (not found)')
.get('/not-a-real-package.json')
.expectBadge({ label: 'msrv', message: 'not found' })

View File

@@ -1,5 +1,5 @@
import { renderVersionBadge } from '../version.js'
import { InvalidResponse, pathParams } from '../index.js'
import { pathParams } from '../index.js'
import { BaseCratesService, description } from './crates-base.js'
export default class CratesVersion extends BaseCratesService {
@@ -20,9 +20,6 @@ export default class CratesVersion extends BaseCratesService {
}
transform(json) {
if (json.errors) {
throw new InvalidResponse({ prettyMessage: json.errors[0].detail })
}
return json.crate.max_stable_version
? json.crate.max_stable_version
: json.crate.max_version

View File

@@ -1,6 +1,4 @@
import { test, given } from 'sazerac'
import { expect } from 'chai'
import { InvalidResponse } from '../index.js'
import CratesVersion from './crates-version.service.js'
describe('CratesVersion', function () {
@@ -10,10 +8,4 @@ describe('CratesVersion', function () {
crate: { max_stable_version: '1.1.0', max_version: '1.9.0-alpha' },
}).expect('1.1.0')
})
it('throws InvalidResponse on error response', function () {
expect(() =>
CratesVersion.prototype.transform({ errors: [{ detail: 'idk how...' }] }),
).to.throw(InvalidResponse)
})
})

View File

@@ -1,8 +1,7 @@
import camelcase from 'camelcase'
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger, optionalUrl } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, queryParams } from '../index.js'
const schemaSingular = Joi.object({
topic_count: nonNegativeInteger,
@@ -24,17 +23,19 @@ const queryParamSchema = Joi.object({
server: optionalUrl.required(),
}).required()
function singular(variant) {
return variant.slice(0, -1)
}
const params = queryParams({
name: 'server',
example: 'https://meta.discourse.org',
required: true,
})
class DiscourseBase extends BaseJsonService {
static category = 'chat'
static buildRoute(metric) {
return {
base: 'discourse',
pattern: metric,
queryParamSchema,
}
}
static defaultBadgeData = { label: 'discourse' }
async fetch({ server }) {
@@ -45,58 +46,61 @@ class DiscourseBase extends BaseJsonService {
}
}
function DiscourseMetricIntegrationFactory({ metricType }) {
// We supply the singular form to more easily check against both schemas.
// But, we use the plural form as the metric name for grammatical reasons.
const metricName = `${metricType}s`
return class DiscourseMetric extends DiscourseBase {
// The space is needed so we get 'DiscourseTopics' rather than
// 'Discoursetopics'. `camelcase()` removes it.
static name = camelcase(`Discourse ${metricName}`, { pascalCase: true })
static route = this.buildRoute(metricName)
class DiscourseMetric extends DiscourseBase {
static route = {
base: 'discourse',
pattern: ':variant(topics|users|posts|likes)',
queryParamSchema,
}
static examples = [
{
title: `Discourse ${metricName}`,
namedParams: {},
queryParams: {
server: 'https://meta.discourse.org',
},
staticPreview: this.render({ stat: 100 }),
},
]
static openApi = {
'/discourse/topics': {
get: { summary: 'Discourse Topics', parameters: params },
},
'/discourse/users': {
get: { summary: 'Discourse Users', parameters: params },
},
'/discourse/posts': {
get: { summary: 'Discourse Posts', parameters: params },
},
'/discourse/likes': {
get: { summary: 'Discourse Likes', parameters: params },
},
}
static render({ stat }) {
return {
message: `${metric(stat)} ${metricName}`,
color: 'brightgreen',
}
static render({ variant, stat }) {
return {
message: `${metric(stat)} ${variant}`,
color: 'brightgreen',
}
}
async handle(_routeParams, { server }) {
const data = await this.fetch({ server })
// e.g. metricType == 'topic' --> try 'topic_count' then 'topics_count'
let stat = data[`${metricType}_count`]
if (stat === undefined) {
stat = data[`${metricType}s_count`]
}
return this.constructor.render({ stat })
async handle({ variant }, { server }) {
const data = await this.fetch({ server })
// e.g. variant == 'topics' --> try 'topic_count' then 'topics_count'
let stat = data[`${singular(variant)}_count`]
if (stat === undefined) {
stat = data[`${variant}_count`]
}
return this.constructor.render({ variant, stat })
}
}
class DiscourseStatus extends DiscourseBase {
static route = this.buildRoute('status')
static examples = [
{
title: 'Discourse status',
namedParams: {},
queryParams: {
server: 'https://meta.discourse.org',
static route = {
base: 'discourse',
pattern: 'status',
queryParamSchema,
}
static openApi = {
'/discourse/status': {
get: {
summary: 'Discourse Status',
parameters: params,
},
staticPreview: this.render(),
},
]
}
static render() {
return {
@@ -113,11 +117,4 @@ class DiscourseStatus extends DiscourseBase {
}
}
const metricIntegrations = [
{ metricType: 'topic' },
{ metricType: 'user' },
{ metricType: 'post' },
{ metricType: 'like' },
].map(DiscourseMetricIntegrationFactory)
export default [...metricIntegrations, DiscourseStatus]
export default [DiscourseMetric, DiscourseStatus]

View File

@@ -1,7 +1,7 @@
import Joi from 'joi'
import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
import { optionalUrl } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, queryParam, pathParam } from '../index.js'
const schema = Joi.object({
status: Joi.alternatives()
@@ -22,48 +22,51 @@ export default class DroneBuild extends BaseJsonService {
}
static auth = { passKey: 'drone_token', serviceKey: 'drone' }
static examples = [
{
title: 'Drone (cloud)',
pattern: ':user/:repo',
namedParams: {
user: 'harness',
repo: 'drone',
static openApi = {
'/drone/build/{user}/{repo}': {
get: {
summary: 'Drone',
parameters: [
pathParam({
name: 'user',
example: 'drone',
}),
pathParam({
name: 'repo',
example: 'autoscaler',
}),
queryParam({
name: 'server',
example: 'https://drone.shields.io',
}),
],
},
staticPreview: renderBuildStatusBadge({ status: 'success' }),
},
{
title: 'Drone (cloud) with branch',
pattern: ':user/:repo/:branch',
namedParams: {
user: 'harness',
repo: 'drone',
branch: 'master',
'/drone/build/{user}/{repo}/{branch}': {
get: {
summary: 'Drone (branch)',
parameters: [
pathParam({
name: 'user',
example: 'drone',
}),
pathParam({
name: 'repo',
example: 'autoscaler',
}),
pathParam({
name: 'branch',
example: 'master',
}),
queryParam({
name: 'server',
example: 'https://drone.shields.io',
}),
],
},
staticPreview: renderBuildStatusBadge({ status: 'success' }),
},
{
title: 'Drone (self-hosted)',
pattern: ':user/:repo',
queryParams: { server: 'https://drone.shields.io' },
namedParams: {
user: 'badges',
repo: 'shields',
},
staticPreview: renderBuildStatusBadge({ status: 'success' }),
},
{
title: 'Drone (self-hosted) with branch',
pattern: ':user/:repo/:branch',
queryParams: { server: 'https://drone.shields.io' },
namedParams: {
user: 'badges',
repo: 'shields',
branch: 'feat/awesome-thing',
},
staticPreview: renderBuildStatusBadge({ status: 'success' }),
},
]
}
static defaultBadgeData = { label: 'build' }

View File

@@ -10,14 +10,14 @@ const isDroneBuildStatus = Joi.alternatives().try(
)
t.create('cloud-hosted build status on default branch')
.get('/harness/drone.json')
.get('/drone/autoscaler.json')
.expectBadge({
label: 'build',
message: isDroneBuildStatus,
})
t.create('cloud-hosted build status on named branch')
.get('/harness/drone/master.json')
.get('/drone/autoscaler/master.json')
.expectBadge({
label: 'build',
message: isDroneBuildStatus,

View File

@@ -43,7 +43,7 @@ t.create('TOML from url | caching with new query params')
t.create('TOML from url | with prefix & suffix & label')
.get(
'.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.database.temp_targets.cpu&prefix=%2B&suffix=°C&label=CPU Temp Target',
'.json?url=https://raw.githubusercontent.com/squirrelchat/smol-toml/mistress/bench/testfiles/toml-spec-example.toml&query=$.database.temp_targets.cpu&prefix=%2B&suffix=%C2%B0C&label=CPU Temp Target',
)
.expectBadge({ label: 'CPU Temp Target', message: '+79.5°C' })

View File

@@ -0,0 +1,107 @@
import { NotFound, pathParams } from '../index.js'
import { renderVersionBadge } from '../version.js'
import GalaxyToolshedService from './galaxytoolshed-base.js'
export class GalaxyToolshedVersion extends GalaxyToolshedService {
static category = 'version'
static route = {
base: 'galaxytoolshed/v',
pattern: ':repository/:owner/:tool?/:requirement?',
}
static openApi = {
'/galaxytoolshed/v/{repository}/{owner}': {
get: {
summary: 'Galaxy Toolshed - Repository Version',
parameters: pathParams(
{
name: 'repository',
example: 'sra_tools',
},
{
name: 'owner',
example: 'iuc',
},
),
},
},
'/galaxytoolshed/v/{repository}/{owner}/{tool}': {
get: {
summary: 'Galaxy Toolshed - Tool Version',
parameters: pathParams(
{
name: 'repository',
example: 'sra_tools',
},
{
name: 'owner',
example: 'iuc',
},
{
name: 'tool',
example: 'fastq_dump',
},
),
},
},
'/galaxytoolshed/v/{repository}/{owner}/{tool}/{requirement}': {
get: {
summary: 'Galaxy Toolshed - Tool Requirement Version',
parameters: pathParams(
{
name: 'repository',
example: 'sra_tools',
},
{
name: 'owner',
example: 'iuc',
},
{
name: 'tool',
example: 'fastq_dump',
},
{
name: 'requirement',
example: 'perl',
},
),
},
},
}
static transform({ response, tool, requirement }) {
if (tool !== undefined) {
const dataTool = response[1].valid_tools.find(x => x.id === tool)
if (dataTool === undefined) {
throw new NotFound({ prettyMessage: 'tool not found' })
}
// Requirement version
if (requirement !== undefined) {
const dataRequirement = dataTool.requirements.find(
x => x.name === requirement,
)
if (dataRequirement === undefined) {
throw new NotFound({ prettyMessage: 'requirement not found' })
}
return dataRequirement.version
}
// Tool version
return dataTool.version
}
// Repository version
return response[1].changeset_revision
}
async handle({ repository, owner, tool, requirement }) {
const response = await this.fetchLastOrderedInstallableRevisionsSchema({
repository,
owner,
})
const version = this.constructor.transform({
response,
tool,
requirement,
})
return renderVersionBadge({ version })
}
}

View File

@@ -0,0 +1,51 @@
import { withRegex, isVPlusTripleDottedVersion } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('version - repository')
.get('/sra_tools/iuc.json')
.expectBadge({
label: 'galaxytoolshed',
message: withRegex(/^([\w\d]+)$/),
})
t.create('version - tool').get('/sra_tools/iuc/fastq_dump.json').expectBadge({
label: 'galaxytoolshed',
message: isVPlusTripleDottedVersion,
})
t.create('version - requirement')
.get('/sra_tools/iuc/fastq_dump/perl.json')
.expectBadge({
label: 'galaxytoolshed',
message: isVPlusTripleDottedVersion,
})
// Not found
t.create('version - changesetRevision not found')
.get('/bioqc/badilla.json')
.expectBadge({
label: 'galaxytoolshed',
message: 'changesetRevision not found',
})
t.create('version - repository not found')
.get('/sra_too/iuc.json')
.expectBadge({
label: 'galaxytoolshed',
message: 'not found',
})
t.create('version - owner not found').get('/sra_tool/iu.json').expectBadge({
label: 'galaxytoolshed',
message: 'not found',
})
t.create('version - tool not found')
.get('/sra_tools/iuc/fastq_dum.json')
.expectBadge({
label: 'galaxytoolshed',
message: 'tool not found',
})
t.create('version - requirement not found')
.get('/sra_tools/iuc/fastq_dump/per.json')
.expectBadge({
label: 'galaxytoolshed',
message: 'requirement not found',
})

View File

@@ -10,7 +10,7 @@ class DummyGiteaService extends GiteaBase {
async handle() {
const data = await this.fetch({
schema: Joi.any(),
url: 'https://codeberg.org/api/v1/repos/CanisHelix/shields-badge-test/releases',
url: 'https://gitea.com/api/v1/repos/CanisHelix/shields-badge-test/releases',
})
return { message: data.message }
}
@@ -24,7 +24,7 @@ describe('GiteaBase', function () {
public: {
services: {
gitea: {
authorizedOrigins: ['https://codeberg.org'],
authorizedOrigins: ['https://gitea.com'],
},
},
},
@@ -34,7 +34,7 @@ describe('GiteaBase', function () {
}
it('sends the auth information as configured', async function () {
const scope = nock('https://codeberg.org')
const scope = nock('https://gitea.com')
.get('/api/v1/repos/CanisHelix/shields-badge-test/releases')
.matchHeader('Authorization', 'Bearer fake-key')
.reply(200, { message: 'fake message' })

View File

@@ -1,5 +1,6 @@
const documentation = `
Note that the gitea_url parameter is required because there is canonical hosted gitea service provided by Gitea.
const description = `
By default this badge looks for repositories on [gitea.com](https://gitea.com).
To specify another instance like [codeberg](https://codeberg.org/), [forgejo](https://forgejo.org/) or a self-hosted instance, use the \`gitea_url\` query param.
`
function httpErrorsFor() {
@@ -9,4 +10,4 @@ function httpErrorsFor() {
}
}
export { documentation, httpErrorsFor }
export { description, httpErrorsFor }

View File

@@ -2,7 +2,7 @@ import Joi from 'joi'
import { nonNegativeInteger, optionalUrl } from '../validators.js'
import { metric } from '../text-formatters.js'
import { pathParam, queryParam } from '../index.js'
import { documentation, httpErrorsFor } from './gitea-helper.js'
import { description, httpErrorsFor } from './gitea-helper.js'
import GiteaBase from './gitea-base.js'
/*
@@ -12,7 +12,7 @@ The keys could be anything and {} is a valid response (e.g: for an empty repo)
const schema = Joi.object().pattern(/./, nonNegativeInteger)
const queryParamSchema = Joi.object({
gitea_url: optionalUrl.required(),
gitea_url: optionalUrl,
}).required()
export default class GiteaLanguageCount extends GiteaBase {
@@ -28,20 +28,19 @@ export default class GiteaLanguageCount extends GiteaBase {
'/gitea/languages/count/{user}/{repo}': {
get: {
summary: 'Gitea language count',
description: documentation,
description,
parameters: [
pathParam({
name: 'user',
example: 'forgejo',
example: 'gitea',
}),
pathParam({
name: 'repo',
example: 'forgejo',
example: 'tea',
}),
queryParam({
name: 'gitea_url',
example: 'https://codeberg.org',
required: true,
example: 'https://gitea.com',
}),
],
},
@@ -58,7 +57,7 @@ export default class GiteaLanguageCount extends GiteaBase {
}
async fetch({ user, repo, baseUrl }) {
// https://try.gitea.io/api/swagger#/repository/repoGetLanguages
// https://gitea.com/api/swagger#/repository/repoGetLanguages
return super.fetch({
schema,
url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`,
@@ -66,7 +65,7 @@ export default class GiteaLanguageCount extends GiteaBase {
})
}
async handle({ user, repo }, { gitea_url: baseUrl }) {
async handle({ user, repo }, { gitea_url: baseUrl = 'https://gitea.com' }) {
const data = await this.fetch({
user,
repo,

View File

@@ -3,7 +3,12 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('language count (empty repo)')
t.create('language count').get('/gitea/tea.json').expectBadge({
label: 'languages',
message: Joi.number().integer().positive(),
})
t.create('language count (empty repo) (self-managed)')
.get(
'/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org',
)
@@ -12,14 +17,14 @@ t.create('language count (empty repo)')
message: '0',
})
t.create('language count')
t.create('language count (self-managed)')
.get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
.expectBadge({
label: 'languages',
message: Joi.number().integer().positive(),
})
t.create('language count (user or repo not found)')
t.create('language count (user or repo not found) (self-managed)')
.get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
.expectBadge({
label: 'languages',

View File

@@ -2,7 +2,7 @@ import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { latest, renderVersionBadge } from '../version.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { documentation, httpErrorsFor } from './gitea-helper.js'
import { description, httpErrorsFor } from './gitea-helper.js'
import GiteaBase from './gitea-base.js'
const schema = Joi.array().items(
@@ -18,7 +18,7 @@ const displayNameEnum = ['tag', 'release']
const dateOrderByEnum = ['created_at', 'published_at']
const queryParamSchema = Joi.object({
gitea_url: optionalUrl.required(),
gitea_url: optionalUrl,
include_prereleases: Joi.equal(''),
sort: Joi.string()
.valid(...sortEnum)
@@ -44,20 +44,19 @@ export default class GiteaRelease extends GiteaBase {
'/gitea/v/release/{user}/{repo}': {
get: {
summary: 'Gitea Release',
description: documentation,
description,
parameters: [
pathParam({
name: 'user',
example: 'forgejo',
example: 'gitea',
}),
pathParam({
name: 'repo',
example: 'forgejo',
example: 'tea',
}),
queryParam({
name: 'gitea_url',
example: 'https://codeberg.org',
required: true,
example: 'https://gitea.com',
}),
queryParam({
name: 'include_prereleases',
@@ -87,7 +86,7 @@ export default class GiteaRelease extends GiteaBase {
static defaultBadgeData = { label: 'release' }
async fetch({ user, repo, baseUrl }) {
// https://try.gitea.io/api/swagger#/repository/repoGetRelease
// https://gitea.com/api/swagger#/repository/repoGetRelease
return super.fetch({
schema,
url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`,
@@ -122,7 +121,7 @@ export default class GiteaRelease extends GiteaBase {
async handle(
{ user, repo },
{
gitea_url: baseUrl,
gitea_url: baseUrl = 'https://gitea.com',
include_prereleases: pre,
sort,
display_name: displayName,

View File

@@ -1,39 +1,48 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Release (latest by date)')
.get('/gitea/tea.json')
.expectBadge({
label: 'release',
message: Joi.string(),
color: Joi.any().valid(...['orange', 'blue']),
})
t.create('Release (latest by date) (self-managed)')
.get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org')
.expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
t.create('Release (latest by date, order by created_at)')
t.create('Release (latest by date, order by created_at) (self-managed)')
.get(
'/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=created_at',
)
.expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
t.create('Release (latest by date, order by published_at)')
t.create('Release (latest by date, order by published_at) (self-managed)')
.get(
'/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=published_at',
)
.expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' })
t.create('Release (latest by semver)')
t.create('Release (latest by semver) (self-managed)')
.get(
'/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver',
)
.expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' })
t.create('Release (latest by semver pre-release)')
t.create('Release (latest by semver pre-release) (self-managed)')
.get(
'/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver&include_prereleases',
)
.expectBadge({ label: 'release', message: 'v5.0.0-rc1', color: 'orange' })
t.create('Release (project not found)')
t.create('Release (project not found) (self-managed)')
.get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org')
.expectBadge({ label: 'release', message: 'user or repo not found' })
t.create('Release (no tags)')
t.create('Release (no tags) (self-managed)')
.get(
'/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org',
)

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
import { BaseSvgScrapingService } from '../index.js'
import { BaseSvgScrapingService, pathParam, queryParam } from '../index.js'
import { documentation } from './github-helpers.js'
const schema = Joi.object({
@@ -14,8 +14,6 @@ const queryParamSchema = Joi.object({
branch: Joi.alternatives().try(Joi.string(), Joi.number().cast('string')),
}).required()
const keywords = ['action', 'actions']
export default class GithubActionsWorkflowStatus extends BaseSvgScrapingService {
static category = 'build'
@@ -25,53 +23,26 @@ export default class GithubActionsWorkflowStatus extends BaseSvgScrapingService
queryParamSchema,
}
static examples = [
{
title: 'GitHub Workflow Status',
namedParams: {
user: 'actions',
repo: 'toolkit',
workflow: 'unit-tests.yml',
static openApi = {
'/github/actions/workflow/status/{user}/{repo}/{workflow}': {
get: {
summary: 'GitHub Actions Workflow Status',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'actions' }),
pathParam({ name: 'repo', example: 'toolkit' }),
pathParam({ name: 'workflow', example: 'unit-tests.yml' }),
queryParam({ name: 'branch', example: 'main' }),
queryParam({
name: 'event',
example: 'push',
description:
'See GitHub Actions [Events that trigger workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) for allowed values.',
}),
],
},
staticPreview: renderBuildStatusBadge({
status: 'passing',
}),
documentation,
keywords,
},
{
title: 'GitHub Workflow Status (with branch)',
namedParams: {
user: 'actions',
repo: 'toolkit',
workflow: 'unit-tests.yml',
},
queryParams: {
branch: 'main',
},
staticPreview: renderBuildStatusBadge({
status: 'passing',
}),
documentation,
keywords,
},
{
title: 'GitHub Workflow Status (with event)',
namedParams: {
user: 'actions',
repo: 'toolkit',
workflow: 'unit-tests.yml',
},
queryParams: {
event: 'push',
},
staticPreview: renderBuildStatusBadge({
status: 'passing',
}),
documentation,
keywords,
},
]
}
static _cacheLength = 60

View File

@@ -1,7 +1,7 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import parseLinkHeader from 'parse-link-header'
import { InvalidResponse } from '../index.js'
import { InvalidResponse, pathParam, queryParam } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
@@ -35,33 +35,51 @@ export default class GitHubCommitActivity extends GithubAuthV4Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub commit activity',
// Override the pattern to omit the deprecated interval "4w".
pattern: ':interval(t|y|m|w)/:user/:repo',
namedParams: { interval: 'm', user: 'eslint', repo: 'eslint' },
queryParams: { authorFilter: 'nzakas' },
staticPreview: this.render({ interval: 'm', commitCount: 457 }),
keywords: ['commits'],
documentation,
},
{
title: 'GitHub commit activity (branch)',
// Override the pattern to omit the deprecated interval "4w".
pattern: ':interval(t|y|m|w)/:user/:repo/:branch*',
namedParams: {
interval: 'm',
user: 'badges',
repo: 'squint',
branch: 'main',
static openApi = {
'/github/commit-activity/{interval}/{user}/{repo}': {
get: {
summary: 'GitHub commit activity',
description: documentation,
parameters: [
pathParam({
name: 'interval',
example: 'm',
description: 'Commits in the last Week, Month, Year, or Total',
schema: {
type: 'string',
// Override the enum to omit the deprecated interval "4w".
enum: ['w', 'm', 'y', 't'],
},
}),
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'squint' }),
queryParam({ name: 'authorFilter', example: 'calebcartwright' }),
],
},
queryParams: { authorFilter: 'calebcartwright' },
staticPreview: this.render({ interval: 'm', commitCount: 5 }),
keywords: ['commits'],
documentation,
},
]
'/github/commit-activity/{interval}/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub commit activity (branch)',
description: documentation,
parameters: [
pathParam({
name: 'interval',
example: 'm',
description: 'Commits in the last Week, Month, Year, or Total',
schema: {
type: 'string',
// Override the enum to omit the deprecated interval "4w".
enum: ['w', 'm', 'y', 't'],
},
}),
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'squint' }),
pathParam({ name: 'branch', example: 'main' }),
queryParam({ name: 'authorFilter', example: 'calebcartwright' }),
],
},
},
}
static defaultBadgeData = { label: 'commit activity', color: 'blue' }

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -19,23 +20,20 @@ export default class GithubCommitsDifference extends GithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub commits difference between two branches/tags/commits',
namedParams: {
user: 'microsoft',
repo: 'vscode',
static openApi = {
'/github/commits-difference/{user}/{repo}': {
get: {
summary: 'GitHub commits difference between two branches/tags/commits',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'microsoft' }),
pathParam({ name: 'repo', example: 'vscode' }),
queryParam({ name: 'base', example: '1.60.0', required: true }),
queryParam({ name: 'head', example: '82f2db7', required: true }),
],
},
queryParams: {
base: '1.60.0',
head: '82f2db7',
},
staticPreview: this.render({
commitCount: 9227,
}),
documentation,
},
]
}
static defaultBadgeData = { label: 'commits difference', namedLogo: 'github' }

View File

@@ -1,15 +1,20 @@
import Joi from 'joi'
import { pathParam } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import {
fetchLatestRelease,
queryParamSchema,
openApiQueryParams,
} from './github-common-release.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
const schema = Joi.object({ ahead_by: nonNegativeInteger }).required()
const latestDocs =
'<p>The <code>include_prereleases</code>, <code>sort</code> and <code>filter</code> params can be used to configure how we determine the latest version.</p>'
export default class GithubCommitsSince extends GithubAuthV3Service {
static category = 'activity'
static route = {
@@ -18,110 +23,60 @@ export default class GithubCommitsSince extends GithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub commits since tagged version',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: '3.4.7',
static openApi = {
'/github/commits-since/{user}/{repo}/{version}': {
get: {
summary: 'GitHub commits since tagged version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'SubtitleEdit' }),
pathParam({ name: 'repo', example: 'subtitleedit' }),
pathParam({
name: 'version',
example: '3.4.7',
}),
],
},
staticPreview: this.render({
version: '3.4.7',
commitCount: 4225,
}),
documentation,
},
{
title: 'GitHub commits since tagged version (branch)',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: '3.4.7',
branch: 'master',
'/github/commits-since/{user}/{repo}/{version}/{branch}': {
get: {
summary: 'GitHub commits since tagged version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'SubtitleEdit' }),
pathParam({ name: 'repo', example: 'subtitleedit' }),
pathParam({
name: 'version',
example: '3.4.7',
}),
pathParam({ name: 'branch', example: 'main' }),
],
},
staticPreview: this.render({
version: '3.4.7',
commitCount: 4225,
}),
documentation,
},
{
title: 'GitHub commits since latest release (by date)',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: 'latest',
'/github/commits-since/{user}/{repo}/latest': {
get: {
summary: 'GitHub commits since latest release',
description: documentation + latestDocs,
parameters: [
pathParam({ name: 'user', example: 'SubtitleEdit' }),
pathParam({ name: 'repo', example: 'subtitleedit' }),
...openApiQueryParams,
],
},
staticPreview: this.render({
version: '3.5.7',
commitCount: 157,
}),
documentation,
},
{
title: 'GitHub commits since latest release (by date) for a branch',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: 'latest',
branch: 'master',
'/github/commits-since/{user}/{repo}/latest/{branch}': {
get: {
summary: 'GitHub commits since latest release (branch)',
description: documentation + latestDocs,
parameters: [
pathParam({ name: 'user', example: 'SubtitleEdit' }),
pathParam({ name: 'repo', example: 'subtitleedit' }),
pathParam({ name: 'branch', example: 'main' }),
...openApiQueryParams,
],
},
staticPreview: this.render({
version: '3.5.7',
commitCount: 157,
}),
documentation,
},
{
title:
'GitHub commits since latest release (by date including pre-releases)',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: 'latest',
},
queryParams: { include_prereleases: null },
staticPreview: this.render({
version: 'v3.5.8-alpha.1',
isPrerelease: true,
commitCount: 158,
}),
documentation,
},
{
title: 'GitHub commits since latest release (by SemVer)',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: 'latest',
},
queryParams: { sort: 'semver' },
staticPreview: this.render({
version: 'v4.0.1',
sort: 'semver',
commitCount: 200,
}),
documentation,
},
{
title:
'GitHub commits since latest release (by SemVer including pre-releases)',
namedParams: {
user: 'SubtitleEdit',
repo: 'subtitleedit',
version: 'latest',
},
queryParams: { sort: 'semver', include_prereleases: null },
staticPreview: this.render({
version: 'v4.0.2-alpha.1',
sort: 'semver',
isPrerelease: true,
commitCount: 201,
}),
documentation,
},
]
}
static defaultBadgeData = { label: 'github', namedLogo: 'github' }

View File

@@ -2,7 +2,7 @@ import Joi from 'joi'
import { matcher } from 'matcher'
import { nonNegativeInteger } from '../validators.js'
import { latest } from '../version.js'
import { NotFound } from '../index.js'
import { NotFound, queryParams } from '../index.js'
import { httpErrorsFor } from './github-helpers.js'
const releaseInfoSchema = Joi.object({
@@ -67,21 +67,37 @@ function getLatestRelease({ releases, sort, includePrereleases }) {
return releases[0]
}
const sortEnum = ['date', 'semver']
const queryParamSchema = Joi.object({
include_prereleases: Joi.equal(''),
sort: Joi.string().valid('date', 'semver').default('date'),
sort: Joi.string()
.valid(...sortEnum)
.default('date'),
filter: Joi.string(),
}).required()
const filterDocs = `<div>
<p>
const filterDocs = `<p>
The <code>filter</code> param can be used to apply a filter to the
project's tag or release names before selecting the latest from the list.
Two constructs are available: <code>*</code> is a wildcard matching zero
or more characters, and if the pattern starts with a <code>!</code>,
the whole pattern is negated.
</p>
</div>`
</p>`
const openApiQueryParams = queryParams(
{
name: 'include_prereleases',
example: null,
schema: { type: 'boolean' },
},
{
name: 'sort',
example: 'semver',
schema: { type: 'string', enum: sortEnum },
},
{ name: 'filter', example: '*beta*', description: filterDocs },
)
function applyFilter({ releases, filter, displayName }) {
if (!filter) {
@@ -137,7 +153,7 @@ async function fetchLatestRelease(
return latestRelease
}
export { fetchLatestRelease, filterDocs, queryParamSchema }
export { fetchLatestRelease, queryParamSchema, openApiQueryParams }
// currently only used for tests
export const _getLatestRelease = getLatestRelease

View File

@@ -1,26 +1,9 @@
import Joi from 'joi'
import gql from 'graphql-tag'
import { metric } from '../text-formatters.js'
import { InvalidParameter } from '../index.js'
import { InvalidParameter, pathParam, queryParam } from '../index.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import {
documentation as commonDocumentation,
transformErrors,
} from './github-helpers.js'
const documentation = `${commonDocumentation}
<p>
<b>Note:</b><br>
1. Parameter <code>type</code> accepts either <code>file</code> or <code>dir</code> value. Passing any other value will result in an error.<br>
2. Parameter <code>extension</code> accepts file extension without a leading dot.
For instance for <code>.js</code> extension pass <code>js</code>.
Only single <code>extension</code> value can be specified.
<code>extension</code> is applicable for <code>type</code> <code>file</code> only.
Passing it either without <code>type</code> or along with <code>type</code> <code>dir</code> will result in an error.<br>
3. GitHub API has an upper limit of 1,000 files for a directory.
In case a directory contains files above the limit, a badge might present inaccurate information.<br>
</p>
`
import { documentation, transformErrors } from './github-helpers.js'
const schema = Joi.object({
data: Joi.object({
@@ -39,11 +22,18 @@ const schema = Joi.object({
}).required(),
}).required()
const typeEnum = ['dir', 'file']
const queryParamSchema = Joi.object({
type: Joi.any().valid('dir', 'file'),
type: Joi.any().valid(...typeEnum),
extension: Joi.string(),
})
const typeDocs =
'Entity to count: directories or files. If not specified, both files and directories are counted. GitHub API has an upper limit of 1,000 files for a directory. If a directory contains files above the limit, the badge will show an inaccurate count.'
const extensionDocs =
'Filter to files of type. Specify the extension without a leading dot. For instance for `.js` extension pass `js`. This param is only applicable if type is `file`'
export default class GithubDirectoryFileCount extends GithubAuthV4Service {
static category = 'size'
@@ -53,62 +43,51 @@ export default class GithubDirectoryFileCount extends GithubAuthV4Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub repo file count',
pattern: ':user/:repo',
namedParams: { user: 'badges', repo: 'shields' },
staticPreview: this.render({ count: 20 }),
documentation,
static openApi = {
'/github/directory-file-count/{user}/{repo}': {
get: {
summary: 'GitHub repo file or directory count',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
queryParam({
name: 'type',
example: 'file',
schema: { type: 'string', enum: typeEnum },
description: typeDocs,
}),
queryParam({
name: 'extension',
example: 'js',
description: extensionDocs,
}),
],
},
},
{
title: 'GitHub repo file count (custom path)',
pattern: ':user/:repo/:path',
namedParams: { user: 'badges', repo: 'shields', path: 'services' },
staticPreview: this.render({ count: 10 }),
documentation,
'/github/directory-file-count/{user}/{repo}/{path}': {
get: {
summary: 'GitHub repo file or directory count (in path)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
pathParam({ name: 'path', example: 'services' }),
queryParam({
name: 'type',
example: 'file',
schema: { type: 'string', enum: typeEnum },
description: typeDocs,
}),
queryParam({
name: 'extension',
example: 'js',
description: extensionDocs,
}),
],
},
},
{
title: 'GitHub repo directory count',
pattern: ':user/:repo',
namedParams: { user: 'badges', repo: 'shields' },
queryParams: { type: 'dir' },
staticPreview: this.render({ count: 8 }),
documentation,
},
{
title: 'GitHub repo directory count (custom path)',
pattern: ':user/:repo/:path',
namedParams: { user: 'badges', repo: 'shields', path: 'services' },
queryParams: { type: 'dir' },
staticPreview: this.render({ count: 8 }),
documentation,
},
{
title: 'GitHub repo file count (file type)',
pattern: ':user/:repo',
namedParams: { user: 'badges', repo: 'shields' },
queryParams: { type: 'file' },
staticPreview: this.render({ count: 2 }),
documentation,
},
{
title: 'GitHub repo file count (custom path & file type)',
pattern: ':user/:repo/:path',
namedParams: { user: 'badges', repo: 'shields', path: 'services' },
queryParams: { type: 'file' },
staticPreview: this.render({ count: 2 }),
documentation,
},
{
title: 'GitHub repo file count (file extension)',
pattern: ':user/:repo/:path',
namedParams: { user: 'badges', repo: 'shields', path: 'services' },
queryParams: { type: 'file', extension: 'js' },
staticPreview: this.render({ count: 1 }),
documentation,
},
]
}
static defaultBadgeData = { color: 'blue', label: 'files' }

View File

@@ -1,15 +1,15 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { documentation, transformErrors } from './github-helpers.js'
const discussionsSearchDocs = `
For a full list of available filters and allowed values that can be used in the <code>query</code>,
For a full list of available filters and allowed values,
see GitHub's documentation on
[Searching discussions](https://docs.github.com/en/search-github/searching-on-github/searching-discussions).
${documentation}
`
const discussionCountSchema = Joi.object({
@@ -56,21 +56,22 @@ class GithubDiscussionsSearch extends BaseGithubDiscussionsSearch {
queryParamSchema,
}
static examples = [
{
title: 'GitHub discussions custom search',
namedParams: {},
queryParams: {
query: 'repo:badges/shields is:answered answered-by:chris48s',
static openApi = {
'/github/discussions-search': {
get: {
summary: 'GitHub discussions custom search',
description: documentation,
parameters: [
queryParam({
name: 'query',
description: discussionsSearchDocs,
example: 'repo:badges/shields is:answered answered-by:chris48s',
required: true,
}),
],
},
staticPreview: {
label: 'query',
message: '2',
color: 'blue',
},
documentation: discussionsSearchDocs,
},
]
}
async handle(namedParams, { query }) {
const discussionCount = await this.fetch({ query })
@@ -85,24 +86,24 @@ class GithubRepoDiscussionsSearch extends BaseGithubDiscussionsSearch {
queryParamSchema,
}
static examples = [
{
title: 'GitHub discussions custom search in repo',
namedParams: {
user: 'badges',
repo: 'shields',
static openApi = {
'/github/discussions-search/{user}/{repo}': {
get: {
summary: 'GitHub discussions custom search in repo',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
queryParam({
name: 'query',
description: discussionsSearchDocs,
example: 'is:answered answered-by:chris48s',
required: true,
}),
],
},
queryParams: {
query: 'is:answered answered-by:chris48s',
},
staticPreview: {
label: 'query',
message: '2',
color: 'blue',
},
documentation: discussionsSearchDocs,
},
]
}
async handle({ user, repo }, { query }) {
query = `repo:${user}/${repo} ${query}`

View File

@@ -1,13 +1,17 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { renderDownloadsBadge } from '../downloads.js'
import { NotFound } from '../index.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { fetchLatestRelease } from './github-common-release.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
const sortEnum = ['date', 'semver']
const queryParamSchema = Joi.object({
sort: Joi.string().valid('date', 'semver').default('date'),
sort: Joi.string()
.valid(...sortEnum)
.default('date'),
}).required()
const releaseSchema = Joi.object({
@@ -24,173 +28,84 @@ const releaseArraySchema = Joi.alternatives().try(
Joi.array().length(0),
)
const variantParam = pathParam({
name: 'variant',
example: 'downloads',
description: 'downloads including or excluding pre-releases',
schema: { type: 'string', enum: ['downloads', 'downloads-pre'] },
})
const userParam = pathParam({ name: 'user', example: 'atom' })
const repoParam = pathParam({ name: 'repo', example: 'atom' })
const tagParam = pathParam({ name: 'tag', example: 'v0.190.0' })
const assetNameParam = pathParam({
name: 'assetName',
example: 'atom-amd64.deb',
})
const sortParam = queryParam({
name: 'sort',
example: 'semver',
schema: { type: 'string', enum: sortEnum },
description: 'Method used to determine latest release. Default: `date`',
})
export default class GithubDownloads extends GithubAuthV3Service {
static category = 'downloads'
static route = {
base: 'github',
pattern: ':kind(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
pattern: ':variant(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
queryParamSchema,
}
static examples = [
{
title: 'GitHub all releases',
pattern: 'downloads/:user/:repo/total',
namedParams: {
user: 'atom',
repo: 'atom',
static openApi = {
'/github/downloads/{user}/{repo}/total': {
get: {
summary: 'GitHub Downloads (all assets, all releases)',
description: documentation,
parameters: [userParam, repoParam],
},
staticPreview: this.render({
assetName: 'total',
downloads: 857000,
}),
documentation,
},
{
title: 'GitHub release (latest by date)',
pattern: 'downloads/:user/:repo/:tag/total',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
'/github/{variant}/{user}/{repo}/latest/total': {
get: {
summary: 'GitHub Downloads (all assets, latest release)',
description: documentation,
parameters: [variantParam, userParam, repoParam, sortParam],
},
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloads: 27000,
}),
documentation,
},
{
title: 'GitHub release (latest by SemVer)',
pattern: 'downloads/:user/:repo/:tag/total',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
'/github/downloads/{user}/{repo}/{tag}/total': {
get: {
summary: 'GitHub Downloads (all assets, specific tag)',
description: documentation,
parameters: [userParam, repoParam, tagParam],
},
queryParams: { sort: 'semver' },
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloads: 27000,
}),
documentation,
},
{
title: 'GitHub release (latest by date including pre-releases)',
pattern: 'downloads-pre/:user/:repo/:tag/total',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
'/github/downloads/{user}/{repo}/{assetName}': {
get: {
summary: 'GitHub Downloads (specific asset, all releases)',
description: documentation,
parameters: [userParam, repoParam, assetNameParam],
},
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloads: 2000,
}),
documentation,
},
{
title: 'GitHub release (latest by SemVer including pre-releases)',
pattern: 'downloads-pre/:user/:repo/:tag/total',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
'/github/{variant}/{user}/{repo}/latest/{assetName}': {
get: {
summary: 'GitHub Downloads (specific asset, latest release)',
description: documentation,
parameters: [
variantParam,
userParam,
repoParam,
assetNameParam,
sortParam,
],
},
queryParams: { sort: 'semver' },
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloads: 2000,
}),
documentation,
},
{
title: 'GitHub release (by tag)',
pattern: 'downloads/:user/:repo/:tag/total',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'v0.190.0',
'/github/downloads/{user}/{repo}/{tag}/{assetName}': {
get: {
summary: 'GitHub Downloads (specific asset, specific tag)',
description: documentation,
parameters: [userParam, repoParam, tagParam, assetNameParam],
},
staticPreview: this.render({
tag: 'v0.190.0',
assetName: 'total',
downloads: 490000,
}),
documentation,
},
{
title: 'GitHub release (latest by date and asset)',
pattern: 'downloads/:user/:repo/:tag/:assetName',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
assetName: 'atom-amd64.deb',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloads: 3000,
}),
documentation,
},
{
title: 'GitHub release (latest by SemVer and asset)',
pattern: 'downloads/:user/:repo/:tag/:assetName',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
assetName: 'atom-amd64.deb',
},
queryParams: { sort: 'semver' },
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloads: 3000,
}),
documentation,
},
{
title: 'GitHub release (latest by date and asset including pre-releases)',
pattern: 'downloads-pre/:user/:repo/:tag/:assetName',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
assetName: 'atom-amd64.deb',
},
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloads: 237,
}),
documentation,
},
{
title:
'GitHub release (latest by SemVer and asset including pre-releases)',
pattern: 'downloads-pre/:user/:repo/:tag/:assetName',
namedParams: {
user: 'atom',
repo: 'atom',
tag: 'latest',
assetName: 'atom-amd64.deb',
},
queryParams: { sort: 'semver' },
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloads: 237,
}),
documentation,
},
]
}
static defaultBadgeData = { label: 'downloads', namedLogo: 'github' }
@@ -219,10 +134,10 @@ export default class GithubDownloads extends GithubAuthV3Service {
return { downloads }
}
async handle({ kind, user, repo, tag, assetName }, { sort }) {
async handle({ variant, user, repo, tag, assetName }, { sort }) {
let releases
if (tag === 'latest') {
const includePre = kind === 'downloads-pre' || undefined
const includePre = variant === 'downloads-pre' || undefined
const latestRelease = await fetchLatestRelease(
this,
{ user, repo },

View File

@@ -10,7 +10,7 @@ const mockLatestRelease = release => nock =>
const mockReleases = releases => nock =>
nock('https://api.github.com')
.get('/repos/photonstorm/phaser/releases')
.get('/repos/photonstorm/phaser/releases?per_page=100')
.reply(200, releases)
t.create('Downloads all releases')

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -11,18 +12,15 @@ const schema = Joi.object({
export default class GithubFollowers extends GithubAuthV3Service {
static category = 'social'
static route = { base: 'github/followers', pattern: ':user' }
static examples = [
{
title: 'GitHub followers',
namedParams: { user: 'espadrine' },
staticPreview: Object.assign(this.render({ followers: 150 }), {
label: 'Follow',
style: 'social',
}),
queryParams: { label: 'Follow' },
documentation,
static openApi = {
'/github/followers/{user}': {
get: {
summary: 'GitHub followers',
description: documentation,
parameters: pathParams({ name: 'user', example: 'espadrine' }),
},
},
]
}
static defaultBadgeData = { label: 'followers', namedLogo: 'github' }

View File

@@ -1,5 +1,6 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
@@ -16,29 +17,18 @@ const schema = Joi.object({
export default class GithubForks extends GithubAuthV4Service {
static category = 'social'
static route = { base: 'github/forks', pattern: ':user/:repo' }
static examples = [
{
title: 'GitHub forks',
namedParams: {
user: 'badges',
repo: 'shields',
static openApi = {
'/github/forks/{user}/{repo}': {
get: {
summary: 'GitHub forks',
description: documentation,
parameters: pathParams(
{ name: 'user', example: 'badges' },
{ name: 'repo', example: 'shields' },
),
},
// TODO: This is currently a literal, as `staticPreview` doesn't
// support `link`.
staticPreview: {
label: 'Fork',
message: '150',
style: 'social',
},
// staticPreview: {
// ...this.render({ user: 'badges', repo: 'shields', forkCount: 150 }),
// label: 'fork',
// style: 'social',
// },
queryParams: { label: 'Fork' },
documentation,
},
]
}
static defaultBadgeData = { label: 'forks', namedLogo: 'github' }

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import { InvalidResponse } from '../index.js'
import { InvalidResponse, pathParam, queryParam } from '../index.js'
import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
import { fetchRepoContent } from './github-common-fetch.js'
import { documentation } from './github-helpers.js'
@@ -11,7 +11,8 @@ const queryParamSchema = Joi.object({
const goVersionRegExp = /^go (.+)$/m
const keywords = ['golang']
const filenameDescription =
'The `filename` param can be used to specify the path to `go.mod`. By default, we look for `go.mod` in the repo root'
export default class GithubGoModGoVersion extends ConditionalGithubAuthV3Service {
static category = 'version'
@@ -21,42 +22,39 @@ export default class GithubGoModGoVersion extends ConditionalGithubAuthV3Service
queryParamSchema,
}
static examples = [
{
title: 'GitHub go.mod Go version',
pattern: ':user/:repo',
namedParams: { user: 'gohugoio', repo: 'hugo' },
staticPreview: this.render({ version: '1.12' }),
documentation,
keywords,
static openApi = {
'/github/go-mod/go-version/{user}/{repo}': {
get: {
summary: 'GitHub go.mod Go version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'gohugoio' }),
pathParam({ name: 'repo', example: 'hugo' }),
queryParam({
name: 'filename',
example: 'src/go.mod',
description: filenameDescription,
}),
],
},
},
{
title: 'GitHub go.mod Go version (branch)',
pattern: ':user/:repo/:branch',
namedParams: { user: 'gohugoio', repo: 'hugo', branch: 'master' },
staticPreview: this.render({ version: '1.12', branch: 'master' }),
documentation,
keywords,
'/github/go-mod/go-version/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub go.mod Go version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'gohugoio' }),
pathParam({ name: 'repo', example: 'hugo' }),
pathParam({ name: 'branch', example: 'master' }),
queryParam({
name: 'filename',
example: 'src/go.mod',
description: filenameDescription,
}),
],
},
},
{
title: 'GitHub go.mod Go version (subdirectory of monorepo)',
pattern: ':user/:repo',
namedParams: { user: 'golang', repo: 'go' },
queryParams: { filename: 'src/go.mod' },
staticPreview: this.render({ version: '1.14' }),
documentation,
keywords,
},
{
title: 'GitHub go.mod Go version (branch & subdirectory of monorepo)',
pattern: ':user/:repo/:branch',
namedParams: { user: 'golang', repo: 'go', branch: 'master' },
queryParams: { filename: 'src/go.mod' },
staticPreview: this.render({ version: '1.14' }),
documentation,
keywords,
},
]
}
static defaultBadgeData = { label: 'Go' }

View File

@@ -1,6 +1,7 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import dayjs from 'dayjs'
import { pathParam, queryParam } from '../index.js'
import { metric, maybePluralize } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
@@ -9,7 +10,7 @@ import {
transformErrors,
} from './github-helpers.js'
const documentation = `
const description = `
This badge is designed for projects hosted on GitHub which are
participating in
[Hacktoberfest](https://hacktoberfest.digitalocean.com),
@@ -55,40 +56,24 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
queryParamSchema,
}
static examples = [
{
title: 'GitHub Hacktoberfest combined status',
namedParams: {
year: '2023',
user: 'snyk',
repo: 'snyk',
static openApi = {
'/github/hacktoberfest/{year}/{user}/{repo}': {
get: {
summary: 'GitHub Hacktoberfest combined status',
description,
parameters: [
pathParam({
name: 'year',
example: '2023',
schema: { type: 'string', enum: this.getEnum('year') },
}),
pathParam({ name: 'user', example: 'tmrowco' }),
pathParam({ name: 'repo', example: 'tmrowapp-contrib' }),
queryParam({ name: 'suggestion_label', example: 'help wanted' }),
],
},
staticPreview: this.render({
suggestedIssueCount: 12,
contributionCount: 8,
daysLeft: 15,
}),
documentation,
},
{
title: 'GitHub Hacktoberfest combined status (suggestion label override)',
namedParams: {
year: '2023',
user: 'tmrowco',
repo: 'tmrowapp-contrib',
},
queryParams: {
suggestion_label: 'help wanted',
},
staticPreview: this.render({
year: '2023',
suggestedIssueCount: 12,
contributionCount: 8,
daysLeft: 15,
}),
documentation,
},
]
}
static defaultBadgeData = { label: 'hacktoberfest', color: 'orange' }

View File

@@ -1,16 +1,15 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { documentation, transformErrors } from './github-helpers.js'
const issuesSearchDocs = `
For a full list of available filters and allowed values that can be used in the <code>query</code>,
For a full list of available filters and allowed values,
see GitHub's documentation on
[Searching issues and pull requests](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests)
${documentation}
`
const issueCountSchema = Joi.object({
@@ -57,21 +56,23 @@ class GithubIssuesSearch extends BaseGithubIssuesSearch {
queryParamSchema,
}
static examples = [
{
title: 'GitHub issue custom search',
namedParams: {},
queryParams: {
query: 'repo:badges/shields is:closed label:bug author:app/sentry-io',
static openApi = {
'/github/issues-search': {
get: {
summary: 'GitHub issue custom search',
description: documentation,
parameters: [
queryParam({
name: 'query',
description: issuesSearchDocs,
example:
'repo:badges/shields is:closed label:bug author:app/sentry-io',
required: true,
}),
],
},
staticPreview: {
label: 'query',
message: '10',
color: 'blue',
},
documentation: issuesSearchDocs,
},
]
}
async handle(namedParams, { query }) {
const issueCount = await this.fetch({ query })
@@ -86,24 +87,24 @@ class GithubRepoIssuesSearch extends BaseGithubIssuesSearch {
queryParamSchema,
}
static examples = [
{
title: 'GitHub issue custom search in repo',
namedParams: {
user: 'badges',
repo: 'shields',
static openApi = {
'/github/issues-search/{user}/{repo}': {
get: {
summary: 'GitHub issue custom search in repo',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
queryParam({
name: 'query',
description: issuesSearchDocs,
example: 'is:closed label:bug author:app/sentry-io',
required: true,
}),
],
},
queryParams: {
query: 'is:closed label:bug author:app/sentry-io',
},
staticPreview: {
label: 'query',
message: '10',
color: 'blue',
},
documentation: issuesSearchDocs,
},
]
}
async handle({ user, repo }, { query }) {
query = `repo:${user}/${repo} ${query}`

View File

@@ -1,12 +1,9 @@
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { age as ageColor } from '../color-formatters.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
const commonExampleAttrs = {
keywords: ['latest'],
documentation,
}
const schema = Joi.array()
.items(
@@ -24,9 +21,11 @@ const schema = Joi.array()
.required()
.min(1)
const displayEnum = ['author', 'committer']
const queryParamSchema = Joi.object({
display_timestamp: Joi.string()
.valid('author', 'committer')
.valid(...displayEnum)
.default('author'),
}).required()
@@ -38,40 +37,41 @@ export default class GithubLastCommit extends GithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub last commit',
pattern: ':user/:repo',
namedParams: {
user: 'google',
repo: 'skia',
static openApi = {
'/github/last-commit/{user}/{repo}': {
get: {
summary: 'GitHub last commit',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'google' }),
pathParam({ name: 'repo', example: 'skia' }),
queryParam({
name: 'display_timestamp',
example: 'committer',
schema: { type: 'string', enum: displayEnum },
description: 'Defaults to `author` if not specified',
}),
],
},
staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
...commonExampleAttrs,
},
{
title: 'GitHub last commit (branch)',
pattern: ':user/:repo/:branch',
namedParams: {
user: 'google',
repo: 'skia',
branch: 'infra/config',
'/github/last-commit/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub last commit (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'google' }),
pathParam({ name: 'repo', example: 'skia' }),
pathParam({ name: 'branch', example: 'infra/config' }),
queryParam({
name: 'display_timestamp',
example: 'committer',
schema: { type: 'string', enum: displayEnum },
description: 'Defaults to `author` if not specified',
}),
],
},
staticPreview: this.render({ commitDate: '2013-07-31T20:01:41Z' }),
...commonExampleAttrs,
},
{
title: 'GitHub last commit (by committer)',
pattern: ':user/:repo',
namedParams: {
user: 'google',
repo: 'skia',
},
queryParams: { display_timestamp: 'committer' },
staticPreview: this.render({ commitDate: '2022-10-15T20:01:41Z' }),
...commonExampleAttrs,
},
]
}
static defaultBadgeData = { label: 'last commit' }

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { renderVersionBadge } from '../version.js'
import {
individualValueSchema,
@@ -27,56 +28,31 @@ class GithubManifestVersion extends ConditionalGithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub manifest version',
pattern: ':user/:repo',
namedParams: {
user: 'sindresorhus',
repo: 'show-all-github-issues',
static openApi = {
'/github/manifest-json/v/{user}/{repo}': {
get: {
summary: 'GitHub manifest version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'sindresorhus' }),
pathParam({ name: 'repo', example: 'show-all-github-issues' }),
queryParam({ name: 'filename', example: 'extension/manifest.json' }),
],
},
staticPreview: this.render({ version: '1.0.3' }),
documentation,
},
{
title: 'GitHub manifest version',
pattern: ':user/:repo/:branch',
namedParams: {
user: 'sindresorhus',
repo: 'show-all-github-issues',
branch: 'master',
'/github/manifest-json/v/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub manifest version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'sindresorhus' }),
pathParam({ name: 'repo', example: 'show-all-github-issues' }),
pathParam({ name: 'branch', example: 'master' }),
queryParam({ name: 'filename', example: 'extension/manifest.json' }),
],
},
staticPreview: this.render({ version: '1.0.3', branch: 'master' }),
documentation,
},
{
title: 'GitHub manifest version (path)',
pattern: ':user/:repo',
namedParams: {
user: 'RedSparr0w',
repo: 'IndieGala-Helper',
},
queryParams: {
filename: 'extension/manifest.json',
},
staticPreview: this.render({ version: 2 }),
documentation,
},
{
title: 'GitHub manifest version (path)',
pattern: ':user/:repo/:branch',
namedParams: {
user: 'RedSparr0w',
repo: 'IndieGala-Helper',
branch: 'master',
},
queryParams: {
filename: 'extension/manifest.json',
},
staticPreview: this.render({ version: 2, branch: 'master' }),
documentation,
},
]
}
static render({ version, branch }) {
return renderVersionBadge({
@@ -106,74 +82,33 @@ class DynamicGithubManifest extends ConditionalGithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub manifest.json dynamic',
pattern: ':key/:user/:repo',
namedParams: {
key: 'permissions',
user: 'sindresorhus',
repo: 'show-all-github-issues',
static openApi = {
'/github/manifest-json/{key}/{user}/{repo}': {
get: {
summary: 'GitHub manifest.json dynamic',
description: documentation,
parameters: [
pathParam({ name: 'key', example: 'permissions' }),
pathParam({ name: 'user', example: 'sindresorhus' }),
pathParam({ name: 'repo', example: 'show-all-github-issues' }),
queryParam({ name: 'filename', example: 'extension/manifest.json' }),
],
},
staticPreview: this.render({
key: 'permissions',
value: ['webRequest', 'webRequestBlocking'],
}),
documentation,
},
{
title: 'GitHub manifest.json dynamic',
pattern: ':key/:user/:repo/:branch',
namedParams: {
key: 'permissions',
user: 'sindresorhus',
repo: 'show-all-github-issues',
branch: 'master',
'/github/manifest-json/{key}/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub manifest.json dynamic (branch)',
description: documentation,
parameters: [
pathParam({ name: 'key', example: 'permissions' }),
pathParam({ name: 'user', example: 'sindresorhus' }),
pathParam({ name: 'repo', example: 'show-all-github-issues' }),
pathParam({ name: 'branch', example: 'main' }),
queryParam({ name: 'filename', example: 'extension/manifest.json' }),
],
},
staticPreview: this.render({
key: 'permissions',
value: ['webRequest', 'webRequestBlocking'],
branch: 'master',
}),
documentation,
},
{
title: 'GitHub manifest.json dynamic (path)',
pattern: ':key/:user/:repo',
namedParams: {
key: 'permissions',
user: 'RedSparr0w',
repo: 'IndieGala-Helper',
},
queryParams: {
filename: 'extension/manifest.json',
},
staticPreview: this.render({
key: 'permissions',
value: ['bundle', 'rollup', 'micro library'],
}),
documentation,
},
{
title: 'GitHub manifest.json dynamic (path)',
pattern: ':key/:user/:repo/:branch',
namedParams: {
key: 'permissions',
user: 'RedSparr0w',
repo: 'IndieGala-Helper',
branch: 'master',
},
queryParams: {
filename: 'extension/manifest.json',
},
staticPreview: this.render({
key: 'permissions',
value: ['bundle', 'rollup', 'micro library'],
branch: 'master',
}),
documentation,
},
]
}
static defaultBadgeData = { label: 'manifest' }

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParam, pathParams, queryParam } from '../index.js'
import { renderVersionBadge } from '../version.js'
import { transformAndValidate, renderDynamicBadge } from '../dynamic-common.js'
import {
@@ -10,8 +11,6 @@ import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
import { fetchJsonFromRepo } from './github-common-fetch.js'
import { documentation } from './github-helpers.js'
const keywords = ['npm', 'node']
const versionSchema = Joi.object({
version: semver,
}).required()
@@ -28,42 +27,31 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthV3Service {
queryParamSchema: subfolderQueryParamSchema,
}
static examples = [
{
title: 'GitHub package.json version',
pattern: ':user/:repo',
namedParams: { user: 'IcedFrisby', repo: 'IcedFrisby' },
staticPreview: this.render({ version: '2.0.0-alpha.2' }),
documentation,
keywords,
},
{
title: 'GitHub package.json version (branch)',
pattern: ':user/:repo/:branch',
namedParams: {
user: 'IcedFrisby',
repo: 'IcedFrisby',
branch: 'master',
static openApi = {
'/github/package-json/v/{user}/{repo}': {
get: {
summary: 'GitHub package.json version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
queryParam({ name: 'filename', example: 'badge-maker/package.json' }),
],
},
staticPreview: this.render({ version: '2.0.0-alpha.2' }),
documentation,
keywords,
},
{
title: 'GitHub package.json version (subfolder of monorepo)',
pattern: ':user/:repo',
namedParams: {
user: 'metabolize',
repo: 'anafanafo',
'/github/package-json/v/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub package.json version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
pathParam({ name: 'branch', example: 'master' }),
queryParam({ name: 'filename', example: 'badge-maker/package.json' }),
],
},
queryParams: {
filename: 'packages/char-width-table-builder/package.json',
},
staticPreview: this.render({ version: '2.0.0' }),
documentation,
keywords,
},
]
}
static render({ version, branch }) {
return renderVersionBadge({
@@ -85,6 +73,9 @@ class GithubPackageJsonVersion extends ConditionalGithubAuthV3Service {
}
}
const packageNameDescription =
'This may be the name of an unscoped package like `package-name` or a [scoped package](https://docs.npmjs.com/about-scopes) like `@author/package-name`'
class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service {
static category = 'platform-support'
static route = {
@@ -94,58 +85,100 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
queryParamSchema: subfolderQueryParamSchema,
}
static examples = [
{
title: 'GitHub package.json dependency version (prod)',
pattern: ':user/:repo/:packageName',
namedParams: {
user: 'developit',
repo: 'microbundle',
packageName: 'rollup',
static openApi = {
'/github/package-json/dependency-version/{user}/{repo}/{packageName}': {
get: {
summary: 'GitHub package.json prod dependency version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
pathParam({
name: 'packageName',
example: 'dayjs',
description: packageNameDescription,
}),
queryParam({
name: 'filename',
example: 'badge-maker/package.json',
}),
],
},
staticPreview: this.render({
dependency: 'rollup',
range: '^0.67.3',
}),
documentation,
keywords,
},
{
title: 'GitHub package.json dependency version (dev dep on branch)',
pattern: ':user/:repo/dev/:scope?/:packageName/:branch*',
namedParams: {
user: 'zeit',
repo: 'next.js',
branch: 'canary',
scope: '@babel',
packageName: 'preset-react',
'/github/package-json/dependency-version/{user}/{repo}/{packageName}/{branch}':
{
get: {
summary: 'GitHub package.json prod dependency version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'badges' }),
pathParam({ name: 'repo', example: 'shields' }),
pathParam({
name: 'packageName',
example: 'dayjs',
description: packageNameDescription,
}),
pathParam({ name: 'branch', example: 'master' }),
queryParam({
name: 'filename',
example: 'badge-maker/package.json',
}),
],
},
},
staticPreview: this.render({
dependency: '@babel/preset-react',
range: '7.0.0',
}),
documentation,
keywords,
},
{
title: 'GitHub package.json dependency version (subfolder of monorepo)',
pattern: ':user/:repo/:packageName',
namedParams: {
user: 'metabolize',
repo: 'anafanafo',
packageName: 'puppeteer',
'/github/package-json/dependency-version/{user}/{repo}/{kind}/{packageName}':
{
get: {
summary: 'GitHub package.json dev/peer/optional dependency version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'gatsbyjs' }),
pathParam({ name: 'repo', example: 'gatsby' }),
pathParam({
name: 'kind',
example: 'dev',
schema: { type: 'string', enum: this.getEnum('kind') },
}),
pathParam({
name: 'packageName',
example: 'cross-env',
description: packageNameDescription,
}),
queryParam({
name: 'filename',
example: 'packages/gatsby-cli/package.json',
}),
],
},
},
queryParams: {
filename: 'packages/char-width-table-builder/package.json',
'/github/package-json/dependency-version/{user}/{repo}/{kind}/{packageName}/{branch}':
{
get: {
summary:
'GitHub package.json dev/peer/optional dependency version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'gatsbyjs' }),
pathParam({ name: 'repo', example: 'gatsby' }),
pathParam({
name: 'kind',
example: 'dev',
schema: { type: 'string', enum: this.getEnum('kind') },
}),
pathParam({
name: 'packageName',
example: 'cross-env',
description: packageNameDescription,
}),
pathParam({ name: 'branch', example: 'master' }),
queryParam({
name: 'filename',
example: 'packages/gatsby-cli/package.json',
}),
],
},
},
staticPreview: this.render({
dependency: 'puppeteer',
range: '^1.14.0',
}),
documentation,
keywords,
},
]
}
static defaultBadgeData = { label: 'dependency' }
@@ -200,40 +233,39 @@ class DynamicGithubPackageJson extends ConditionalGithubAuthV3Service {
pattern: ':key/:user/:repo/:branch*',
}
static examples = [
{
title: 'GitHub package.json dynamic',
pattern: ':key/:user/:repo',
namedParams: {
key: 'keywords',
user: 'developit',
repo: 'microbundle',
static openApi = {
'/github/package-json/{key}/{user}/{repo}': {
get: {
summary: 'GitHub package.json dynamic',
description: documentation,
parameters: pathParams(
{
name: 'key',
example: 'keywords',
description: 'any key in package.json',
},
{ name: 'user', example: 'developit' },
{ name: 'repo', example: 'microbundle' },
),
},
staticPreview: this.render({
key: 'keywords',
value: ['bundle', 'rollup', 'micro library'],
}),
documentation,
keywords,
},
{
title: 'GitHub package.json dynamic',
pattern: ':key/:user/:repo/:branch',
namedParams: {
key: 'keywords',
user: 'developit',
repo: 'microbundle',
branch: 'master',
'/github/package-json/{key}/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub package.json dynamic (branch)',
description: documentation,
parameters: pathParams(
{
name: 'key',
example: 'keywords',
description: 'any key in package.json',
},
{ name: 'user', example: 'developit' },
{ name: 'repo', example: 'microbundle' },
{ name: 'branch', example: 'master' },
),
},
staticPreview: this.render({
key: 'keywords',
value: ['bundle', 'rollup', 'micro library'],
branch: 'master',
}),
documentation,
keywords,
},
]
}
static defaultBadgeData = { label: 'package.json' }

View File

@@ -2,8 +2,8 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('github pull request check state')
.get('/s/pulls/badges/shields/8486.json')
.expectBadge({ label: 'checks', message: 'failure' })
.get('/s/pulls/badges/shields/9863.json')
.expectBadge({ label: 'checks', message: 'success' })
t.create('github pull request check state (pull request not found)')
.get('/s/pulls/badges/shields/5101.json')
@@ -26,5 +26,5 @@ t.create(
})
t.create('github pull request check contexts')
.get('/contexts/pulls/badges/shields/8486.json')
.expectBadge({ label: 'checks', message: '2 success, 4 failure' })
.get('/contexts/pulls/badges/shields/9863.json')
.expectBadge({ label: 'checks', message: '1 success' })

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import { InvalidResponse } from '../index.js'
import { InvalidResponse, pathParam, queryParam } from '../index.js'
import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
import { fetchRepoContent } from './github-common-fetch.js'
import { documentation } from './github-helpers.js'
@@ -11,6 +11,9 @@ const queryParamSchema = Joi.object({
const versionRegExp = /^Version:[\s]*(.+)$/m
const filenameDescription =
'The `filename` param can be used to specify the path to `DESCRIPTION`. By default, we look for `DESCRIPTION` in the repo root'
export default class GithubRPackageVersion extends ConditionalGithubAuthV3Service {
static category = 'version'
@@ -20,38 +23,39 @@ export default class GithubRPackageVersion extends ConditionalGithubAuthV3Servic
queryParamSchema,
}
static examples = [
{
title: 'GitHub R package version',
pattern: ':user/:repo',
namedParams: { user: 'mixOmicsTeam', repo: 'mixOmics' },
staticPreview: this.render({ version: '6.10.9' }),
documentation,
static openApi = {
'/github/r-package/v/{user}/{repo}': {
get: {
summary: 'GitHub R package version',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'mixOmicsTeam' }),
pathParam({ name: 'repo', example: 'mixOmics' }),
queryParam({
name: 'filename',
example: 'subdirectory/DESCRIPTION',
description: filenameDescription,
}),
],
},
},
{
title: 'GitHub R package version (branch)',
pattern: ':user/:repo/:branch',
namedParams: { user: 'mixOmicsTeam', repo: 'mixOmics', branch: 'master' },
staticPreview: this.render({ version: '6.10.9', branch: 'master' }),
documentation,
'/github/r-package/v/{user}/{repo}/{branch}': {
get: {
summary: 'GitHub R package version (branch)',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'mixOmicsTeam' }),
pathParam({ name: 'repo', example: 'mixOmics' }),
pathParam({ name: 'branch', example: 'master' }),
queryParam({
name: 'filename',
example: 'subdirectory/DESCRIPTION',
description: filenameDescription,
}),
],
},
},
{
title: 'GitHub R package version (subdirectory of monorepo)',
pattern: ':user/:repo',
namedParams: { user: 'mixOmicsTeam', repo: 'mixOmics' },
queryParams: { filename: 'subdirectory/DESCRIPTION' },
staticPreview: this.render({ version: '6.10.9' }),
documentation,
},
{
title: 'GitHub R package version (branch & subdirectory of monorepo)',
pattern: ':user/:repo/:branch',
namedParams: { user: 'mixOmicsTeam', repo: 'mixOmics', branch: 'master' },
queryParams: { filename: 'subdirectory/DESCRIPTION' },
staticPreview: this.render({ version: '6.10.9', branch: 'master' }),
documentation,
},
]
}
static defaultBadgeData = { label: 'R' }

View File

@@ -1,17 +1,20 @@
import Joi from 'joi'
import { addv } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { redirector } from '../index.js'
import { redirector, pathParam, queryParam } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import {
fetchLatestRelease,
filterDocs,
queryParamSchema,
openApiQueryParams,
} from './github-common-release.js'
import { documentation } from './github-helpers.js'
const displayNameEnum = ['tag', 'release']
const extendedQueryParamSchema = Joi.object({
display_name: Joi.string().valid('tag', 'release').default('tag'),
display_name: Joi.string()
.valid(...displayNameEnum)
.default('tag'),
})
class GithubRelease extends GithubAuthV3Service {
@@ -22,86 +25,24 @@ class GithubRelease extends GithubAuthV3Service {
queryParamSchema: queryParamSchema.concat(extendedQueryParamSchema),
}
static examples = [
{
title: 'GitHub release (latest by date)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { display_name: 'tag' },
staticPreview: this.render({
version: 'v4.16.4',
sort: 'date',
isPrerelease: false,
}),
documentation,
},
{
title: 'GitHub release (latest by date including pre-releases)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { include_prereleases: null, display_name: 'tag' },
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'date',
isPrerelease: true,
}),
documentation,
},
{
title: 'GitHub release (latest SemVer)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { sort: 'semver', display_name: 'tag' },
staticPreview: this.render({
version: 'v4.16.4',
sort: 'semver',
isPrerelease: false,
}),
documentation,
},
{
title: 'GitHub release (latest SemVer including pre-releases)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: {
sort: 'semver',
include_prereleases: null,
display_name: 'tag',
static openApi = {
'/github/v/release/{user}/{repo}': {
get: {
summary: 'GitHub Release',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'expressjs' }),
pathParam({ name: 'repo', example: 'express' }),
...openApiQueryParams,
queryParam({
name: 'display_name',
example: 'tag',
schema: { type: 'string', enum: displayNameEnum },
}),
],
},
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'semver',
isPrerelease: true,
}),
documentation,
},
{
title: 'GitHub release (release name instead of tag name)',
namedParams: { user: 'gooddata', repo: 'gooddata-java' },
queryParams: {
sort: 'date',
include_prereleases: null,
display_name: 'release',
},
staticPreview: this.render({
version: '3.7.0+api3',
sort: 'date',
isPrerelease: true,
}),
documentation,
},
{
title: 'GitHub release (with filter)',
namedParams: { user: 'RetroMusicPlayer', repo: 'RetroMusicPlayer' },
queryParams: {
sort: 'date',
display_name: 'release',
filter: '*Open Beta',
},
staticPreview: this.render({
version: 'Release v6.0.2 - Open Beta',
sort: 'date',
isPrerelease: false,
}),
documentation: documentation + filterDocs,
},
]
}
static defaultBadgeData = { label: 'release', namedLogo: 'github' }

View File

@@ -1,7 +1,7 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { nonNegativeInteger } from '../validators.js'
import { NotFound } from '../index.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
@@ -25,33 +25,24 @@ export default class GithubSize extends GithubAuthV3Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub file size in bytes',
namedParams: {
user: 'webcaetano',
repo: 'craft',
path: 'build/phaser-craft.min.js',
},
staticPreview: this.render({ size: 9170 }),
keywords: ['repo'],
documentation,
},
{
title: 'GitHub file size in bytes on a specified ref (branch/commit/tag)',
namedParams: {
user: 'webcaetano',
repo: 'craft',
path: 'build/phaser-craft.min.js',
},
staticPreview: this.render({ size: 9170 }),
keywords: ['repo'],
documentation,
queryParams: {
branch: 'master',
static openApi = {
'/github/size/{user}/{repo}/{path}': {
get: {
summary: 'GitHub file size in bytes',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'webcaetano' }),
pathParam({ name: 'repo', example: 'craft' }),
pathParam({ name: 'path', example: 'build/phaser-craft.min.js' }),
queryParam({
name: 'branch',
example: 'master',
description: 'Can be a branch, a tag or a commit hash.',
}),
],
},
},
]
}
static render({ size }) {
return {

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -16,24 +17,18 @@ export default class GithubStars extends GithubAuthV3Service {
pattern: ':user/:repo',
}
static examples = [
{
title: 'GitHub Repo stars',
namedParams: {
user: 'badges',
repo: 'shields',
static openApi = {
'/github/stars/{user}/{repo}': {
get: {
summary: 'GitHub Repo stars',
description: documentation,
parameters: pathParams(
{ name: 'user', example: 'badges' },
{ name: 'repo', example: 'shields' },
),
},
queryParams: { style: 'social' },
// TODO: This is currently a literal, as `staticPreview` doesn't
// support `link`.
staticPreview: {
label: 'Stars',
message: '7k',
style: 'social',
},
documentation,
},
]
}
static defaultBadgeData = {
label: 'stars',

View File

@@ -4,9 +4,12 @@ import { matcher } from 'matcher'
import { addv } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { latest } from '../version.js'
import { NotFound, redirector } from '../index.js'
import { NotFound, redirector, pathParam } from '../index.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { filterDocs, queryParamSchema } from './github-common-release.js'
import {
queryParamSchema,
openApiQueryParams,
} from './github-common-release.js'
import { documentation, transformErrors } from './github-helpers.js'
const schema = Joi.object({
@@ -34,44 +37,19 @@ class GithubTag extends GithubAuthV4Service {
queryParamSchema,
}
static examples = [
{
title: 'GitHub tag (latest by date)',
namedParams: { user: 'expressjs', repo: 'express' },
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'date',
}),
documentation,
static openApi = {
'/github/v/tag/{user}/{repo}': {
get: {
summary: 'GitHub Tag',
description: documentation,
parameters: [
pathParam({ name: 'user', example: 'expressjs' }),
pathParam({ name: 'repo', example: 'express' }),
...openApiQueryParams,
],
},
},
{
title: 'GitHub tag (latest SemVer)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { sort: 'semver' },
staticPreview: this.render({ version: 'v4.16.4', sort: 'semver' }),
documentation,
},
{
title: 'GitHub tag (latest SemVer pre-release)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { sort: 'semver', include_prereleases: null },
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'semver',
}),
documentation,
},
{
title: 'GitHub tag (with filter)',
namedParams: { user: 'badges', repo: 'shields' },
queryParams: { filter: '!server-*' },
staticPreview: this.render({
version: 'v3.3.1',
sort: 'date',
}),
documentation: documentation + filterDocs,
},
]
}
static defaultBadgeData = {
label: 'tag',

View File

@@ -1,5 +1,6 @@
import Joi from 'joi'
import gql from 'graphql-tag'
import { pathParam, queryParam } from '../index.js'
import { nonNegativeInteger } from '../validators.js'
import { metric } from '../text-formatters.js'
import { GithubAuthV4Service } from './github-auth-service.js'
@@ -10,22 +11,15 @@ import {
const MAX_REPO_LIMIT = 200
const customDocumentation = `This badge takes into account up to <code>${MAX_REPO_LIMIT}</code> of the most starred repositories of given user / org.`
const userDocumentation = `${commonDocumentation}
const description = `${commonDocumentation}
<p>
<b>Note:</b><br>
1. ${customDocumentation}<br>
2. <code>affiliations</code> query param accepts three values (must be UPPER case) <code>OWNER</code>, <code>COLLABORATOR</code>, <code>ORGANIZATION_MEMBER</code>.
One can pass comma separated combinations of these values (no spaces) e.g. <code>OWNER,COLLABORATOR</code> or <code>OWNER,COLLABORATOR,ORGANIZATION_MEMBER</code>.
Default value is <code>OWNER</code>. See the explanation of these values <a href="https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation">here</a>.
</p>
`
const orgDocumentation = `${commonDocumentation}
<p>
<b>Note:</b> ${customDocumentation}
<b>Note:</b> This badge takes into account up to <code>${MAX_REPO_LIMIT}</code> of the most starred repositories of given user / org.
</p>`
const affiliationsDescription = `This param accepts three values (must be UPPER case) <code>OWNER</code>, <code>COLLABORATOR</code>, <code>ORGANIZATION_MEMBER</code>.
One can pass comma separated combinations of these values (no spaces) e.g. <code>OWNER,COLLABORATOR</code> or <code>OWNER,COLLABORATOR,ORGANIZATION_MEMBER</code>.
Default value is <code>OWNER</code>. See the explanation of these values <a href="https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation">here</a>.`
const pageInfoSchema = Joi.object({
hasNextPage: Joi.boolean().required(),
endCursor: Joi.string().allow(null).required(),
@@ -141,34 +135,29 @@ export default class GithubTotalStarService extends GithubAuthV4Service {
queryParamSchema,
}
static examples = [
{
title: "GitHub User's stars",
namedParams: {
user: 'chris48s',
static openApi = {
'/github/stars/{user}': {
get: {
summary: "GitHub User's stars",
description,
parameters: [
pathParam({ name: 'user', example: 'chris48s' }),
queryParam({
name: 'affiliations',
example: 'OWNER,COLLABORATOR',
description: affiliationsDescription,
}),
],
},
queryParams: { affiliations: 'OWNER,COLLABORATOR' },
staticPreview: {
label: this.defaultLabel,
message: 54,
style: 'social',
},
documentation: userDocumentation,
},
{
title: "GitHub Org's stars",
pattern: ':org',
namedParams: {
org: 'badges',
'/github/stars/{org}': {
get: {
summary: "GitHub Org's stars",
description,
parameters: [pathParam({ name: 'org', example: 'badges' })],
},
staticPreview: {
label: this.defaultLabel,
message: metric(7000),
style: 'social',
},
documentation: orgDocumentation,
},
]
}
static defaultBadgeData = {
label: this.defaultLabel,

View File

@@ -1,4 +1,5 @@
import Joi from 'joi'
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -16,24 +17,18 @@ export default class GithubWatchers extends GithubAuthV3Service {
pattern: ':user/:repo',
}
static examples = [
{
title: 'GitHub watchers',
namedParams: {
user: 'badges',
repo: 'shields',
static openApi = {
'/github/watchers/{user}/{repo}': {
get: {
summary: 'GitHub watchers',
description: documentation,
parameters: pathParams(
{ name: 'user', example: 'badges' },
{ name: 'repo', example: 'shields' },
),
},
// TODO: This is currently a literal, as `staticPreview` doesn't
// support `link`.
staticPreview: {
label: 'Watch',
message: '96',
style: 'social',
},
queryParams: { label: 'Watch' },
documentation,
},
]
}
static defaultBadgeData = {
label: 'watchers',

View File

@@ -1,5 +1,5 @@
import { redirector } from '../index.js'
import { documentation } from '../maven-metadata/maven-metadata.js'
import { redirector, pathParam, queryParam } from '../index.js'
import { description } from '../maven-metadata/maven-metadata.js'
export default redirector({
category: 'version',
@@ -8,24 +8,27 @@ export default redirector({
base: 'gradle-plugin-portal/v',
pattern: ':pluginId',
},
examples: [
{
title: 'Gradle Plugin Portal',
queryParams: {
versionSuffix: '.1',
versionPrefix: '0.10',
openApi: {
'/gradle-plugin-portal/v/{pluginId}': {
get: {
summary: 'Gradle Plugin Portal Version',
description,
parameters: [
pathParam({ name: 'pluginId', example: 'com.gradle.plugin-publish' }),
queryParam({
name: 'versionPrefix',
example: '0.10',
description: 'Filter only versions with this prefix.',
}),
queryParam({
name: 'versionSuffix',
example: '.1',
description: 'Filter only versions with this suffix.',
}),
],
},
namedParams: {
pluginId: 'com.gradle.plugin-publish',
},
staticPreview: {
label: 'plugin portal',
message: 'v0.10.1',
color: 'blue',
},
documentation,
},
],
},
transformPath: () => '/maven-metadata/v',
transformQueryParams: ({ pluginId }) => {
const groupPath = pluginId.replace(/\./g, '/')

View File

@@ -0,0 +1,37 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
const description = `
<p><a href="https://hangar.papermc.io/">Hangar</a> is a plugin repository for the Paper, Waterfall and Folia platforms.</p>`
const resourceSchema = Joi.object({
stats: Joi.object({
views: Joi.number().required(),
downloads: Joi.number().required(),
recentViews: Joi.number().required(),
recentDownloads: Joi.number().required(),
stars: Joi.number().required(),
watchers: Joi.number().required(),
}).required(),
}).required()
class BaseHangarService extends BaseJsonService {
static _cacheLength = 3600
async fetch({
slug,
schema = resourceSchema,
url = `https://hangar.papermc.io/api/v1/projects/${slug}`,
}) {
return this._requestJson({
schema,
url,
httpErrors: {
401: 'Api session missing, invalid or expired',
403: 'Not enough permission to use this endpoint',
},
})
}
}
export { description, BaseHangarService }

View File

@@ -0,0 +1,34 @@
import { pathParams } from '../index.js'
import { renderDownloadsBadge } from '../downloads.js'
import { BaseHangarService, description } from './hangar-base.js'
export default class HangarDownloads extends BaseHangarService {
static category = 'downloads'
static route = {
base: 'hangar/dt',
pattern: ':slug',
}
static openApi = {
'/hangar/dt/{slug}': {
get: {
summary: 'Hangar Downloads',
description,
parameters: pathParams({
name: 'slug',
example: 'Essentials',
}),
},
},
}
static defaultBadgeData = { label: 'downloads' }
async handle({ slug }) {
const {
stats: { downloads },
} = await this.fetch({ slug })
return renderDownloadsBadge({ downloads })
}
}

View File

@@ -0,0 +1,13 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Essentials').get('/Essentials.json').expectBadge({
label: 'downloads',
message: isMetric,
})
t.create('Invalid Resource').get('/1.json').expectBadge({
label: 'downloads',
message: 'not found',
})

View File

@@ -0,0 +1,43 @@
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { BaseHangarService, description } from './hangar-base.js'
export default class HangarStars extends BaseHangarService {
static category = 'social'
static route = {
base: 'hangar/stars',
pattern: ':slug',
}
static openApi = {
'/hangar/stars/{slug}': {
get: {
summary: 'Hangar Stars',
description,
parameters: pathParams({
name: 'slug',
example: 'Essentials',
}),
},
},
}
static defaultBadgeData = {
label: 'stars',
color: 'blue',
}
static render({ stars }) {
return {
message: metric(stars),
}
}
async handle({ slug }) {
const {
stats: { stars },
} = await this.fetch({ slug })
return this.constructor.render({ stars })
}
}

View File

@@ -0,0 +1,13 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Essentials').get('/Essentials.json').expectBadge({
label: 'stars',
message: isMetric,
})
t.create('Invalid Resource').get('/1.json').expectBadge({
label: 'stars',
message: 'not found',
})

View File

@@ -0,0 +1,43 @@
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { BaseHangarService, description } from './hangar-base.js'
export default class HangarViews extends BaseHangarService {
static category = 'other'
static route = {
base: 'hangar/views',
pattern: ':slug',
}
static openApi = {
'/hangar/views/{slug}': {
get: {
summary: 'Hangar Views',
description,
parameters: pathParams({
name: 'slug',
example: 'Essentials',
}),
},
},
}
static defaultBadgeData = {
label: 'views',
color: 'blue',
}
static render({ views }) {
return {
message: metric(views),
}
}
async handle({ slug }) {
const {
stats: { views },
} = await this.fetch({ slug })
return this.constructor.render({ views })
}
}

View File

@@ -0,0 +1,13 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Essentials').get('/Essentials.json').expectBadge({
label: 'views',
message: isMetric,
})
t.create('Invalid Resource').get('/1.json').expectBadge({
label: 'views',
message: 'not found',
})

View File

@@ -0,0 +1,43 @@
import { pathParams } from '../index.js'
import { metric } from '../text-formatters.js'
import { BaseHangarService, description } from './hangar-base.js'
export default class HangarWatchers extends BaseHangarService {
static category = 'social'
static route = {
base: 'hangar/watchers',
pattern: ':slug',
}
static openApi = {
'/hangar/watchers/{slug}': {
get: {
summary: 'Hangar Watchers',
description,
parameters: pathParams({
name: 'slug',
example: 'Essentials',
}),
},
},
}
static defaultBadgeData = {
label: 'watchers',
color: 'blue',
}
static render({ watchers }) {
return {
message: metric(watchers),
}
}
async handle({ slug }) {
const {
stats: { watchers },
} = await this.fetch({ slug })
return this.constructor.render({ watchers })
}
}

View File

@@ -0,0 +1,13 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Essentials').get('/Essentials.json').expectBadge({
label: 'watchers',
message: isMetric,
})
t.create('Invalid Resource').get('/1.json').expectBadge({
label: 'watchers',
message: 'not found',
})

View File

@@ -1,18 +1,16 @@
import Joi from 'joi'
import { BaseJsonService, InvalidResponse } from '../index.js'
import {
BaseJsonService,
InvalidResponse,
queryParam,
pathParam,
} from '../index.js'
import { coveragePercentage } from '../color-formatters.js'
const keywords = [
'l10n',
'i18n',
'localization',
'internationalization',
'translation',
'translations',
]
const documentation = `
const description = `
<p>
<a href="https://localizely.com/" target="_blank">Localizely</a> is a management system for translation, localization, and internationalization of your projects.
<br/>
The <b>read-only</b> API token from the Localizely account is required to fetch necessary data.
<br/>
<br/>
@@ -58,40 +56,58 @@ export default class Localizely extends BaseJsonService {
queryParamSchema,
}
static examples = [
{
title: 'Localizely overall progress',
keywords,
documentation,
namedParams: {
projectId: '5cc34208-0418-40b1-8353-acc70c95f802',
branch: 'main',
static openApi = {
'/localizely/progress/{projectId}': {
get: {
summary: 'Localizely progress',
description,
parameters: [
pathParam({
name: 'projectId',
example: '5cc34208-0418-40b1-8353-acc70c95f802',
}),
queryParam({
name: 'token',
example:
'0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
required: true,
}),
queryParam({
name: 'languageCode',
example: 'en-US',
required: false,
}),
],
},
queryParams: {
token:
'0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
},
staticPreview: this.render({ reviewedProgress: 93 }),
},
{
title: 'Localizely language progress',
keywords,
documentation,
namedParams: {
projectId: '5cc34208-0418-40b1-8353-acc70c95f802',
branch: 'main',
'/localizely/progress/{projectId}/{branch}': {
get: {
summary: 'Localizely progress (branch)',
description,
parameters: [
pathParam({
name: 'projectId',
example: '5cc34208-0418-40b1-8353-acc70c95f802',
}),
pathParam({
name: 'branch',
example: 'main',
}),
queryParam({
name: 'token',
example:
'0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
required: true,
}),
queryParam({
name: 'languageCode',
example: 'en-US',
required: false,
}),
],
},
queryParams: {
token:
'0f4d5e31a44f48dcbab966c52cfb0a67c5f1982186c14b85ab389a031dbc225a',
languageCode: 'en-US',
},
staticPreview: this.render({
langName: 'English (US)',
reviewedProgress: 97,
}),
},
]
}
static defaultBadgeData = { label: 'localized' }

View File

@@ -1,5 +1,5 @@
import { redirector } from '../index.js'
import { documentation } from '../maven-metadata/maven-metadata.js'
import { redirector, pathParam, queryParam } from '../index.js'
import { description } from '../maven-metadata/maven-metadata.js'
export default redirector({
category: 'version',
@@ -8,26 +8,28 @@ export default redirector({
base: 'maven-central/v',
pattern: ':groupId/:artifactId/:versionPrefix?',
},
examples: [
{
title: 'Maven Central',
pattern: ':groupId/:artifactId',
queryParams: {
versionSuffix: '-android',
versionPrefix: '29',
openApi: {
'/maven-central/v/{groupId}/{artifactId}': {
get: {
summary: 'Maven Central Version',
description,
parameters: [
pathParam({ name: 'groupId', example: 'com.google.guava' }),
pathParam({ name: 'artifactId', example: 'guava' }),
queryParam({
name: 'versionPrefix',
example: '29',
description: 'Filter only versions with this prefix.',
}),
queryParam({
name: 'versionSuffix',
example: '-android',
description: 'Filter only versions with this suffix.',
}),
],
},
namedParams: {
groupId: 'com.google.guava',
artifactId: 'guava',
},
staticPreview: {
label: 'maven-central',
message: 'v29.0-android',
color: 'blue',
},
documentation,
},
],
},
transformPath: () => '/maven-metadata/v',
transformQueryParams: ({ groupId, artifactId, versionPrefix }) => {
const group = encodeURIComponent(groupId).replace(/\./g, '/')

View File

@@ -2,7 +2,7 @@
// the file contains common constants for badges uses maven-metadata
export const documentation = `
export const description = `
<p>
<code>versionPrefix</code> and <code>versionSuffix</code> allow narrowing down
the range of versions the badge will take into account,

View File

@@ -1,8 +1,8 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { renderVersionBadge } from '../version.js'
import { BaseXmlService, NotFound } from '../index.js'
import { documentation } from './maven-metadata.js'
import { BaseXmlService, NotFound, queryParams } from '../index.js'
import { description } from './maven-metadata.js'
const queryParamSchema = Joi.object({
metadataUrl: optionalUrl.required(),
@@ -29,20 +29,32 @@ export default class MavenMetadata extends BaseXmlService {
queryParamSchema,
}
static examples = [
{
title: 'Maven metadata URL',
namedParams: {},
queryParams: {
metadataUrl:
'https://repo1.maven.org/maven2/com/google/guava/guava/maven-metadata.xml',
versionPrefix: '29.',
versionSuffix: '-android',
static openApi = {
'/maven-metadata/v': {
get: {
summary: 'Maven metadata URL',
description,
parameters: queryParams(
{
name: 'metadataUrl',
example:
'https://repo1.maven.org/maven2/com/google/guava/guava/maven-metadata.xml',
required: true,
},
{
name: 'versionPrefix',
example: '29',
description: 'Filter only versions with this prefix.',
},
{
name: 'versionSuffix',
example: '-android',
description: 'Filter only versions with this suffix.',
},
),
},
staticPreview: renderVersionBadge({ version: '29.0-android' }),
documentation,
},
]
}
static defaultBadgeData = {
label: 'maven',

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, queryParam, pathParam } from '../index.js'
const schema = Joi.object({
state: Joi.string()
@@ -25,11 +25,13 @@ const queryParamSchema = Joi.object({
publish: Joi.equal(''),
}).required()
const documentation = `
const description = `
The [Mozilla HTTP Observatory](https://observatory.mozilla.org)
is a set of tools to analyze your website
is a set of security tools to analyze your website
and inform you if you are utilizing the many available methods to secure it.
`
const publishDescription = `
By default the scan result is hidden from the public result list.
You can activate the publication of the scan result
by setting the \`publish\` parameter.
@@ -50,21 +52,31 @@ export default class MozillaObservatory extends BaseJsonService {
queryParamSchema,
}
static examples = [
{
title: 'Mozilla HTTP Observatory Grade',
namedParams: { format: 'grade', host: 'github.com' },
staticPreview: this.render({
format: 'grade',
state: 'FINISHED',
grade: 'A+',
score: 115,
}),
queryParams: { publish: null },
keywords: ['scanner', 'security'],
documentation,
static openApi = {
'/mozilla-observatory/{format}/{host}': {
get: {
summary: 'Mozilla HTTP Observatory Grade',
description,
parameters: [
pathParam({
name: 'format',
example: 'grade',
schema: { type: 'string', enum: this.getEnum('format') },
}),
pathParam({
name: 'host',
example: 'github.com',
}),
queryParam({
name: 'publish',
schema: { type: 'boolean' },
example: null,
description: publishDescription,
}),
],
},
},
]
}
static defaultBadgeData = {
label: 'observatory',

View File

@@ -1,3 +1,4 @@
import { pathParams } from '../index.js'
import { createServiceFamily } from '../nuget/nuget-v3-service-family.js'
const { NugetVersionService: Version, NugetDownloadService: Downloads } =
@@ -8,51 +9,73 @@ const { NugetVersionService: Version, NugetDownloadService: Downloads } =
})
class MyGetVersionService extends Version {
static examples = [
{
title: 'MyGet',
pattern: 'myget/:feed/v/:packageName',
namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
staticPreview: this.render({ version: '2.6.1' }),
},
{
title: 'MyGet (with prereleases)',
pattern: 'myget/:feed/vpre/:packageName',
namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
staticPreview: this.render({ version: '2.7.0-beta0001' }),
},
{
title: 'MyGet tenant',
pattern: ':tenant.myget/:feed/v/:packageName',
namedParams: {
tenant: 'tizen',
feed: 'dotnet',
packageName: 'Tizen.NET',
static openApi = {
'/myget/{feed}/{variant}/{packageName}': {
get: {
summary: 'MyGet Version',
parameters: pathParams(
{ name: 'feed', example: 'mongodb' },
{
name: 'variant',
example: 'v',
schema: { type: 'variant', enum: ['v', 'vpre'] },
description:
'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
},
{ name: 'packageName', example: 'MongoDB.Driver.Core' },
),
},
staticPreview: this.render({ version: '9.0.0.16564' }),
},
]
'/{tenant}/{feed}/{variant}/{packageName}': {
get: {
summary: 'MyGet Version (tenant)',
parameters: pathParams(
{
name: 'tenant',
example: 'tizen.myget',
description: 'MyGet Tenant in the format `name.myget`',
},
{ name: 'feed', example: 'dotnet' },
{
name: 'variant',
example: 'v',
schema: { type: 'variant', enum: ['v', 'vpre'] },
description:
'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
},
{ name: 'packageName', example: 'Tizen.NET' },
),
},
},
}
}
class MyGetDownloadService extends Downloads {
static examples = [
{
title: 'MyGet',
pattern: 'myget/:feed/dt/:packageName',
namedParams: { feed: 'mongodb', packageName: 'MongoDB.Driver.Core' },
staticPreview: this.render({ downloads: 419 }),
},
{
title: 'MyGet tenant',
pattern: ':tenant.myget/:feed/dt/:packageName',
namedParams: {
tenant: 'cefsharp',
feed: 'cefsharp',
packageName: 'CefSharp.Common',
static openApi = {
'/myget/{feed}/dt/{packageName}': {
get: {
summary: 'MyGet Downloads',
parameters: pathParams(
{ name: 'feed', example: 'mongodb' },
{ name: 'packageName', example: 'MongoDB.Driver.Core' },
),
},
staticPreview: this.render({ downloads: 9748 }),
},
]
'/{tenant}/{feed}/dt/{packageName}': {
get: {
summary: 'MyGet Downloads (tenant)',
parameters: pathParams(
{
name: 'tenant',
example: 'tizen.myget',
description: 'MyGet Tenant in the format `name.myget`',
},
{ name: 'feed', example: 'dotnet' },
{ name: 'packageName', example: 'Tizen.NET' },
),
},
},
}
}
export { MyGetVersionService, MyGetDownloadService }

View File

@@ -0,0 +1,67 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { BaseJsonService, pathParams } from '../index.js'
const npubSchema = Joi.object({
followers_pubkey_count: Joi.number().required(),
}).required()
const mainSchema = Joi.object({
stats: Joi.object()
.pattern(Joi.string(), npubSchema)
.min(1)
.max(1)
.required(),
}).required()
export default class NostrBandFollowers extends BaseJsonService {
static category = 'social'
static route = {
base: 'nostr-band/followers',
pattern: ':npub',
}
static openApi = {
'/nostr-band/followers/{npub}': {
get: {
summary: 'Nostr.band Followers',
description:
'Returns the number of followers for a Nostr pubkey using the Nostr.band API.',
parameters: pathParams({
name: 'npub',
description: 'Nostr pubkey in (npub1...) format or hex.',
example:
'npub18c556t7n8xa3df2q82rwxejfglw5przds7sqvefylzjh8tjne28qld0we7',
}),
},
},
}
static defaultBadgeData = { label: 'followers' }
static render({ followers }) {
return {
message: metric(followers),
style: 'social',
}
}
async fetch({ npub }) {
const data = await this._requestJson({
url: `https://api.nostr.band/v0/stats/profile/${npub}`,
schema: mainSchema,
httpErrors: {
400: 'invalid pubkey',
},
})
const stats = data.stats
const firstKey = Object.keys(stats)[0]
return stats[firstKey].followers_pubkey_count
}
async handle({ npub }) {
const followers = await this.fetch({ npub })
return this.constructor.render({ followers })
}
}

View File

@@ -0,0 +1,15 @@
import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('fetch: valid npub')
.get('/npub18c556t7n8xa3df2q82rwxejfglw5przds7sqvefylzjh8tjne28qld0we7.json')
.expectBadge({
label: 'followers',
message: isMetric,
})
t.create('fetch: invalid npub').get('/invalidnpub.json').expectBadge({
label: 'followers',
message: 'invalid pubkey',
})

View File

@@ -34,6 +34,9 @@ export const queryParamSchema = Joi.object({
registry_uri: optionalUrl,
}).required()
export const packageNameDescription =
'This may be the name of an unscoped package like `package-name` or a [scoped package](https://docs.npmjs.com/about-scopes) like `@author/package-name`'
// Abstract class for NPM badges which display data about the latest version
// of a package.
export default class NpmBase extends BaseJsonService {

View File

@@ -1,30 +1,30 @@
import { pathParam, queryParam } from '../index.js'
import { renderContributorBadge } from '../contributor-count.js'
import NpmBase from './npm-base.js'
const keywords = ['node']
import NpmBase, { packageNameDescription } from './npm-base.js'
export default class NpmCollaborators extends NpmBase {
static category = 'activity'
static route = this.buildRoute('npm/collaborators', { withTag: false })
static examples = [
{
title: 'npm collaborators',
pattern: ':packageName',
namedParams: { packageName: 'prettier' },
staticPreview: this.render({ collaborators: 6 }),
keywords,
static openApi = {
'/npm/collaborators/{packageName}': {
get: {
summary: 'NPM Collaborators',
parameters: [
pathParam({
name: 'packageName',
example: 'prettier',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
{
title: 'npm collaborators',
pattern: ':packageName',
namedParams: { packageName: 'prettier' },
queryParams: { registry_uri: 'https://registry.npmjs.com' },
staticPreview: this.render({ collaborators: 6 }),
keywords,
},
]
}
static defaultBadgeData = {
label: 'npm collaborators',

View File

@@ -1,8 +1,9 @@
import { pathParam, queryParam } from '../index.js'
import { getDependencyVersion } from '../package-json-helpers.js'
import NpmBase from './npm-base.js'
const { queryParamSchema } = NpmBase
const keywords = ['node']
import NpmBase, {
queryParamSchema,
packageNameDescription,
} from './npm-base.js'
export default class NpmDependencyVersion extends NpmBase {
static category = 'platform-support'
@@ -14,89 +15,55 @@ export default class NpmDependencyVersion extends NpmBase {
queryParamSchema,
}
static examples = [
{
title: 'npm peer dependency version',
pattern: ':packageName/peer/:dependency',
namedParams: {
packageName: 'react-boxplot',
dependency: 'prop-types',
static openApi = {
'/npm/dependency-version/{packageName}/{dependency}': {
get: {
summary: 'NPM (prod) Dependency Version',
parameters: [
pathParam({
name: 'packageName',
example: 'react-boxplot',
description: packageNameDescription,
}),
pathParam({
name: 'dependency',
example: 'simple-statistics',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
staticPreview: this.render({
dependency: 'prop-types',
range: '^15.5.4',
}),
keywords,
},
{
title: 'npm peer dependency version (scoped)',
pattern: ':scope?/:packageName/peer/:dependencyScope?/:dependency',
namedParams: {
scope: '@swellaby',
packageName: 'eslint-config',
dependency: 'eslint',
'/npm/dependency-version/{packageName}/{kind}/{dependency}': {
get: {
summary: 'NPM dev or peer Dependency Version',
parameters: [
pathParam({
name: 'packageName',
example: 'react-boxplot',
description: packageNameDescription,
}),
pathParam({
name: 'kind',
example: 'dev',
schema: { type: 'string', enum: this.getEnum('kind') },
}),
pathParam({
name: 'dependency',
example: 'prop-types',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
staticPreview: this.render({
dependency: 'eslint',
range: '^3.0.0',
}),
keywords,
},
{
title: 'npm dev dependency version',
pattern: ':packageName/dev/:dependency',
namedParams: {
packageName: 'react-boxplot',
dependency: 'eslint-config-standard',
},
staticPreview: this.render({
dependency: 'eslint-config-standard',
range: '^12.0.0',
}),
keywords,
},
{
title: 'npm dev dependency version (scoped)',
pattern: ':scope?/:packageName/dev/:dependencyScope?/:dependency',
namedParams: {
packageName: 'mocha',
dependencyScope: '@mocha',
dependency: 'contributors',
},
staticPreview: this.render({
dependency: '@mocha/contributors',
range: '^1.0.3',
}),
keywords,
},
{
title: 'npm (prod) dependency version',
pattern: ':packageName/:dependency',
namedParams: {
packageName: 'react-boxplot',
dependency: 'simple-statistics',
},
staticPreview: this.render({
dependency: 'simple-statistics',
range: '^6.1.1',
}),
keywords,
},
{
title: 'npm (prod) dependency version (scoped)',
pattern: ':scope?/:packageName/:dependencyScope?/:dependency',
namedParams: {
packageName: 'got',
dependencyScope: '@sindresorhus',
dependency: 'is',
},
staticPreview: this.render({
dependency: '@sindresorhus/is',
range: '^0.15.0',
}),
keywords,
},
]
}
static defaultBadgeData = {
label: 'dependency',

View File

@@ -1,7 +1,8 @@
import Joi from 'joi'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, pathParams } from '../index.js'
import { packageNameDescription } from './npm-base.js'
// https://github.com/npm/registry/blob/master/docs/download-counts.md#output
const pointResponseSchema = Joi.object({
@@ -50,14 +51,26 @@ export default class NpmDownloads extends BaseJsonService {
pattern: ':interval(dw|dm|dy|dt)/:scope(@.+)?/:packageName',
}
static examples = [
{
title: 'npm',
namedParams: { interval: 'dw', packageName: 'localeval' },
staticPreview: this.render({ interval: 'dw', downloadCount: 30000 }),
keywords: ['node'],
static openApi = {
'/npm/{interval}/{packageName}': {
get: {
summary: 'NPM Downloads',
parameters: pathParams(
{
name: 'interval',
example: 'dw',
description: 'Weekly, Monthly, Yearly, or Total downloads',
schema: { type: 'string', enum: this.getEnum('interval') },
},
{
name: 'packageName',
example: 'localeval',
description: packageNameDescription,
},
),
},
},
]
}
// For testing.
static _intervalMap = intervalMap

View File

@@ -1,29 +1,31 @@
import { pathParam, queryParam } from '../index.js'
import { renderLicenseBadge } from '../licenses.js'
import toArray from '../../core/base-service/to-array.js'
import NpmBase from './npm-base.js'
import NpmBase, { packageNameDescription } from './npm-base.js'
export default class NpmLicense extends NpmBase {
static category = 'license'
static route = this.buildRoute('npm/l', { withTag: false })
static examples = [
{
title: 'NPM',
pattern: ':packageName',
namedParams: { packageName: 'express' },
staticPreview: this.render({ licenses: ['MIT'] }),
keywords: ['node'],
static openApi = {
'/npm/l/{packageName}': {
get: {
summary: 'NPM License',
parameters: [
pathParam({
name: 'packageName',
example: 'express',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
{
title: 'NPM',
pattern: ':packageName',
namedParams: { packageName: 'express' },
queryParams: { registry_uri: 'https://registry.npmjs.com' },
staticPreview: this.render({ licenses: ['MIT'] }),
keywords: ['node'],
},
]
}
static render({ licenses }) {
return renderLicenseBadge({ licenses })

View File

@@ -1,5 +1,5 @@
import { pathParams } from '../index.js'
import NpmBase from './npm-base.js'
import { pathParam, queryParam } from '../index.js'
import NpmBase, { packageNameDescription } from './npm-base.js'
// For this badge to correctly detect type definitions, either the relevant
// dependencies must be declared, or the `types` key must be set in
@@ -12,11 +12,18 @@ export default class NpmTypeDefinitions extends NpmBase {
static openApi = {
'/npm/types/{packageName}': {
get: {
summary: 'npm type definitions',
parameters: pathParams({
name: 'packageName',
example: 'chalk',
}),
summary: 'NPM Type Definitions',
parameters: [
pathParam({
name: 'packageName',
example: 'chalk',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
}

View File

@@ -1,9 +1,7 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import { NotFound } from '../index.js'
import NpmBase from './npm-base.js'
const keywords = ['node']
import { NotFound, pathParam, queryParam } from '../index.js'
import NpmBase, { packageNameDescription } from './npm-base.js'
// Joi.string should be a semver.
const schema = Joi.object()
@@ -16,44 +14,44 @@ export default class NpmVersion extends NpmBase {
static route = this.buildRoute('npm/v', { withTag: true })
static examples = [
{
title: 'npm',
pattern: ':packageName',
namedParams: { packageName: 'npm' },
staticPreview: this.render({ version: '6.3.0' }),
keywords,
static openApi = {
'/npm/v/{packageName}': {
get: {
summary: 'NPM Version',
parameters: [
pathParam({
name: 'packageName',
example: 'npm',
description: packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
{
title: 'npm (scoped)',
pattern: ':scope/:packageName',
namedParams: { scope: '@cycle', packageName: 'core' },
staticPreview: this.render({ version: '7.0.0' }),
keywords,
'/npm/v/{packageName}/{tag}': {
get: {
summary: 'NPM Version (with dist tag)',
parameters: [
pathParam({
name: 'packageName',
example: 'npm',
description: packageNameDescription,
}),
pathParam({
name: 'tag',
example: 'next-8',
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
{
title: 'npm (tag)',
pattern: ':packageName/:tag',
namedParams: { packageName: 'npm', tag: 'next-8' },
staticPreview: this.render({ tag: 'latest', version: '6.3.0' }),
keywords,
},
{
title: 'npm (custom registry)',
pattern: ':packageName/:tag',
namedParams: { packageName: 'npm', tag: 'next-8' },
queryParams: { registry_uri: 'https://registry.npmjs.com' },
staticPreview: this.render({ tag: 'latest', version: '7.0.0' }),
keywords,
},
{
title: 'npm (scoped with tag)',
pattern: ':scope/:packageName/:tag',
namedParams: { scope: '@cycle', packageName: 'core', tag: 'canary' },
staticPreview: this.render({ tag: 'latest', version: '6.3.0' }),
keywords,
},
]
}
static defaultBadgeData = {
label: 'npm',

View File

@@ -5,6 +5,9 @@ import {
BaseXmlService,
NotFound,
redirector,
pathParams,
pathParam,
queryParam,
} from '../index.js'
import {
renderVersionBadge,
@@ -138,22 +141,25 @@ function createServiceFamily({
queryParamSchema,
}
static get examples() {
if (!title) return []
static get openApi() {
if (!title) return {}
return [
{
title: `${title} Version`,
namedParams: { packageName: examplePackageName },
staticPreview: this.render({ version: exampleVersion }),
const key = `/${serviceBaseUrl}/v/{packageName}`
const route = {}
route[key] = {
get: {
summary: `${title} Version`,
parameters: [
pathParam({ name: 'packageName', example: examplePackageName }),
queryParam({
name: 'include_prereleases',
schema: { type: 'boolean' },
example: null,
}),
],
},
{
title: `${title} Version (including pre-releases)`,
namedParams: { packageName: examplePackageName },
queryParams: { include_prereleases: null },
staticPreview: this.render({ version: examplePrereleaseVersion }),
},
]
}
return route
}
static defaultBadgeData = {
@@ -199,16 +205,21 @@ function createServiceFamily({
pattern: 'dt/:packageName',
}
static get examples() {
if (!title) return []
static get openApi() {
if (!title) return {}
return [
{
title,
namedParams: { packageName: examplePackageName },
staticPreview: this.render({ downloads: exampleDownloadCount }),
const key = `/${serviceBaseUrl}/dt/{packageName}`
const route = {}
route[key] = {
get: {
summary: `${title} Downloads`,
parameters: pathParams({
name: 'packageName',
example: examplePackageName,
}),
},
]
}
return route
}
static render(props) {

View File

@@ -121,7 +121,7 @@ function createServiceFamily({
.push('(.+?)', 'packageName')
.toObject()
static examples = []
static openApi = {}
static defaultBadgeData = {
label: defaultLabel,
@@ -170,7 +170,7 @@ function createServiceFamily({
.push('(.+?)', 'packageName')
.toObject()
static examples = []
static openApi = {}
static render(props) {
return renderDownloadBadge(props)

View File

@@ -1,3 +1,4 @@
import { pathParams } from '../index.js'
import { createServiceFamily } from './nuget-v3-service-family.js'
const { NugetVersionService: Version, NugetDownloadService: Downloads } =
@@ -10,31 +11,37 @@ const { NugetVersionService: Version, NugetDownloadService: Downloads } =
})
class NugetVersionService extends Version {
static examples = [
{
title: 'Nuget',
pattern: 'v/:packageName',
namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
staticPreview: this.render({ version: '5.2.4' }),
static openApi = {
'/nuget/{variant}/{packageName}': {
get: {
summary: 'NuGet Version',
parameters: pathParams(
{
name: 'variant',
example: 'v',
schema: { type: 'variant', enum: ['v', 'vpre'] },
description:
'Latest stable version (`v`) or Latest version including prereleases (`vpre`).',
},
{ name: 'packageName', example: 'Microsoft.AspNet.Mvc' },
),
},
},
{
title: 'Nuget (with prereleases)',
pattern: 'vpre/:packageName',
namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
staticPreview: this.render({ version: '5.2.5-preview1' }),
},
]
}
}
class NugetDownloadService extends Downloads {
static examples = [
{
title: 'Nuget',
pattern: 'dt/:packageName',
namedParams: { packageName: 'Microsoft.AspNet.Mvc' },
staticPreview: this.render({ downloads: 49e6 }),
static openApi = {
'/nuget/dt/{packageName}': {
get: {
summary: 'NuGet Downloads',
parameters: pathParams({
name: 'packageName',
example: 'Microsoft.AspNet.Mvc',
}),
},
},
]
}
}
export { NugetVersionService, NugetDownloadService }

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import { BaseXmlService } from '../index.js'
import { BaseXmlService, pathParam, queryParam } from '../index.js'
import { optionalUrl } from '../validators.js'
import { isBuildStatus, renderBuildStatusBadge } from './obs-build-status.js'
@@ -26,23 +26,22 @@ export default class ObsService extends BaseXmlService {
isRequired: true,
}
static examples = [
{
title: 'OBS package build status',
namedParams: {
project: 'openSUSE:Tools',
packageName: 'osc',
repository: 'Debian_11',
arch: 'x86_64',
static openApi = {
'/obs/{project}/{packageName}/{repository}/{arch}': {
get: {
summary: 'OBS package build status',
description:
'[Open Build Service](https://openbuildservice.org/) (OBS) is a generic system to build and distribute binary packages',
parameters: [
pathParam({ name: 'project', example: 'openSUSE:Tools' }),
pathParam({ name: 'packageName', example: 'osc' }),
pathParam({ name: 'repository', example: 'Debian_11' }),
pathParam({ name: 'arch', example: 'x86_64' }),
queryParam({ name: 'instance', example: 'https://api.opensuse.org' }),
],
},
queryParams: { instance: 'https://api.opensuse.org' },
staticPreview: this.render({
repository: 'Debian_11',
status: 'succeeded',
}),
keywords: ['open build service'],
},
]
}
static defaultBadgeData = { label: 'build' }

View File

@@ -1,9 +1,7 @@
import Joi from 'joi'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
const keywords = ['sublime', 'sublimetext', 'packagecontrol']
import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
installs: Joi.object({
@@ -20,92 +18,91 @@ const schema = Joi.object({
}).required(),
})
function DownloadsForInterval(downloadInterval) {
const { base, interval, transform, name } = {
day: {
base: 'packagecontrol/dd',
interval: 'day',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// use the downloads from yesterday
downloads += platform.totals[1]
})
return downloads
},
name: 'PackageControlDownloadsDay',
},
week: {
base: 'packagecontrol/dw',
interval: 'week',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 7 days
for (let i = 0; i < 7; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
name: 'PackageControlDownloadsWeek',
},
month: {
base: 'packagecontrol/dm',
interval: 'month',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 30 days
for (let i = 0; i < 30; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
name: 'PackageControlDownloadsMonth',
},
total: {
base: 'packagecontrol/dt',
transform: resp => resp.installs.total,
name: 'PackageControlDownloadsTotal',
},
}[downloadInterval]
return class PackageControlDownloads extends BaseJsonService {
static name = name
static category = 'downloads'
static route = { base, pattern: ':packageName' }
static examples = [
{
title: 'Package Control',
namedParams: { packageName: 'GitGutter' },
staticPreview: renderDownloadsBadge({ downloads: 12000 }),
keywords,
},
]
static defaultBadgeData = { label: 'downloads' }
async fetch({ packageName }) {
const url = `https://packagecontrol.io/packages/${packageName}.json`
return this._requestJson({ schema, url })
}
async handle({ packageName }) {
const data = await this.fetch({ packageName })
return renderDownloadsBadge({
downloads: transform(data),
interval,
const intervalMap = {
dd: {
label: 'day',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// use the downloads from yesterday
downloads += platform.totals[1]
})
}
}
return downloads
},
},
dw: {
label: 'week',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 7 days
for (let i = 0; i < 7; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
},
dm: {
label: 'month',
transform: resp => {
const platforms = resp.installs.daily.data
let downloads = 0
platforms.forEach(platform => {
// total for the first 30 days
for (let i = 0; i < 30; i++) {
downloads += platform.totals[i]
}
})
return downloads
},
},
dt: {
transform: resp => resp.installs.total,
},
}
export default ['day', 'week', 'month', 'total'].map(DownloadsForInterval)
export default class PackageControlDownloads extends BaseJsonService {
static category = 'downloads'
static route = {
base: 'packagecontrol',
pattern: ':interval(dd|dw|dm|dt)/:packageName',
}
static openApi = {
'/packagecontrol/{interval}/{packageName}': {
get: {
summary: 'Package Control Downloads',
description:
'Package Control is a package registry for Sublime Text packages',
parameters: pathParams(
{
name: 'interval',
example: 'dt',
schema: { type: 'string', enum: this.getEnum('interval') },
description: 'Daily, Weekly, Monthly, or Total downloads',
},
{ name: 'packageName', example: 'GitGutter' },
),
},
},
}
static defaultBadgeData = { label: 'downloads' }
async fetch({ packageName }) {
const url = `https://packagecontrol.io/packages/${packageName}.json`
return this._requestJson({ schema, url })
}
async handle({ interval, packageName }) {
const data = await this.fetch({ packageName })
return renderDownloadsBadge({
downloads: intervalMap[interval].transform(data),
interval: intervalMap[interval].label,
})
}
}

View File

@@ -1,16 +1,19 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { coveragePercentage } from '../color-formatters.js'
import { BaseJsonService, InvalidResponse } from '../index.js'
import {
BaseJsonService,
InvalidResponse,
pathParam,
queryParam,
} from '../index.js'
const documentation = `
You must specify the read-only API token from the POEditor account to which the project belongs.
const description = `
POEditor is an web-based tool for translation and internationalization
As per [the POEditor API documentation](https://poeditor.com/docs/api)
> All requests to the API must contain the parameter api_token.
> You can get a read-only key from your POEditor account.
> You'll find it in [My Account > API Access](https://poeditor.com/account/api).
All requests to must contain the parameter \`token\`.
You can get a read-only token from your POEditor account in [My Account > API Access](https://poeditor.com/account/api).
This token will be exposed as part of the badge URL so be sure to generate a read-only token.
`
const schema = Joi.object({
@@ -42,20 +45,25 @@ export default class POEditor extends BaseJsonService {
queryParamSchema,
}
static examples = [
{
title: 'POEditor',
namedParams: { projectId: '323337', languageCode: 'fr' },
queryParams: { token: 'abc123def456' },
staticPreview: this.render({
code: 200,
message: 'OK',
language: { percentage: 93, code: 'fr', name: 'French' },
}),
keywords: ['l10n'],
documentation,
static openApi = {
'/poeditor/progress/{projectId}/{languageCode}': {
get: {
summary: 'POEditor',
description,
parameters: [
pathParam({ name: 'projectId', example: '323337' }),
pathParam({ name: 'languageCode', example: 'fr' }),
queryParam({
name: 'token',
example: 'abc123def456',
description:
'A read-only token from your POEditor account from [My Account > API Access](https://poeditor.com/account/api)',
required: true,
}),
],
},
},
]
}
static render({ code, message, language }) {
if (code !== 200) {

View File

@@ -1,10 +1,8 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, pathParams } from '../index.js'
import { renderDownloadsBadge } from '../downloads.js'
const keywords = ['python']
const schema = Joi.object({
data: Joi.object({
last_day: nonNegativeInteger,
@@ -38,20 +36,24 @@ export default class PypiDownloads extends BaseJsonService {
pattern: ':period(dd|dw|dm)/:packageName',
}
static examples = [
{
title: 'PyPI - Downloads',
namedParams: {
period: 'dd',
packageName: 'Django',
static openApi = {
'/pypi/{period}/{packageName}': {
get: {
summary: 'PyPI - Downloads',
description:
'Python package downloads from [pypistats](https://pypistats.org/)',
parameters: pathParams(
{
name: 'period',
example: 'dd',
schema: { type: 'string', enum: this.getEnum('period') },
description: 'Daily, Weekly, or Monthly downloads',
},
{ name: 'packageName', example: 'Django' },
),
},
staticPreview: renderDownloadsBadge({
interval: 'day',
downloads: 14000,
}),
keywords,
},
]
}
static _cacheLength = 28800

View File

@@ -1,4 +1,4 @@
import { InvalidResponse } from '../index.js'
import { InvalidResponse, pathParams } from '../index.js'
import PypiBase from './pypi-base.js'
import { sortPypiVersions, parseClassifiers } from './pypi-helpers.js'
@@ -37,7 +37,7 @@ const frameworkNameMap = {
},
}
const documentation = `
const description = `
<p>
This service currently support the following Frameworks: <br/>
${Object.values(frameworkNameMap).map(obj => ` <strong>${obj.name}</strong>`)}
@@ -53,21 +53,22 @@ export default class PypiFrameworkVersion extends PypiBase {
)})/:packageName+`,
}
static examples = [
{
title: 'PyPI - Versions from Framework Classifiers',
namedParams: {
frameworkName: 'Plone',
packageName: 'plone.volto',
static openApi = {
'/pypi/frameworkversions/{frameworkName}/{packageName}': {
get: {
summary: 'PyPI - Versions from Framework Classifiers',
description,
parameters: pathParams(
{
name: 'frameworkName',
example: 'plone',
schema: { type: 'string', enum: Object.keys(frameworkNameMap) },
},
{ name: 'packageName', example: 'plone.volto' },
),
},
staticPreview: this.render({
name: 'Plone',
versions: ['5.2', '6.0'],
}),
keywords: ['python'],
documentation,
},
]
}
static defaultBadgeData = { label: 'versions' }

View File

@@ -1,5 +1,6 @@
import Joi from 'joi'
import BaseTomlService from '../../core/base-service/base-toml.js'
import { queryParams } from '../index.js'
import { optionalUrl } from '../validators.js'
const queryParamSchema = Joi.object({
@@ -12,7 +13,7 @@ const schema = Joi.object({
}).required(),
}).required()
const documentation = `Shows the required python version for a package based on the values in the requires-python field in PEP 621 compliant pyproject.toml \n
const description = `Shows the required python version for a package based on the values in the requires-python field in PEP 621 compliant pyproject.toml \n
a URL of the toml is required, please note that when linking to files in github or similar sites, provide URL to raw file, for example:
Use https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml \n
@@ -28,18 +29,20 @@ class PythonVersionFromToml extends BaseTomlService {
queryParamSchema,
}
static examples = [
{
title: 'Python Version from PEP 621 TOML',
namedParams: {},
queryParams: {
tomlFilePath:
'https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml',
static openApi = {
'/python/required-version-toml': {
get: {
summary: 'Python Version from PEP 621 TOML',
description,
parameters: queryParams({
name: 'tomlFilePath',
example:
'https://raw.githubusercontent.com/numpy/numpy/main/pyproject.toml',
required: true,
}),
},
staticPreview: this.render({ requiresPythonString: '>=3.9' }),
documentation,
},
]
}
static defaultBadgeData = { label: 'python' }

View File

@@ -12,10 +12,12 @@ export const t = new ServiceTester({
// downloads
t.create('total downloads (valid)').get('/dt/ReSharper.Nuke.json').expectBadge({
label: 'downloads',
message: isMetric,
})
t.create('total downloads (valid)')
.get('/dt/StyleCop.StyleCop.json')
.expectBadge({
label: 'downloads',
message: isMetric,
})
t.create('total downloads (not found)')
.get('/dt/not-a-real-package.json')
@@ -23,7 +25,7 @@ t.create('total downloads (not found)')
// version
t.create('version (valid)').get('/v/ReSharper.Nuke.json').expectBadge({
t.create('version (valid)').get('/v/StyleCop.StyleCop.json').expectBadge({
label: 'resharper',
message: isVPlusDottedVersionNClauses,
})
@@ -35,7 +37,7 @@ t.create('version (not found)')
// version (pre)
t.create('version (pre) (valid)')
.get('/v/ReSharper.Nuke.json?include_prereleases')
.get('/v/StyleCop.StyleCop.json?include_prereleases')
.expectBadge({
label: 'resharper',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
@@ -46,5 +48,5 @@ t.create('version (pre) (not found)')
.expectBadge({ label: 'resharper', message: 'not found' })
t.create('version (legacy redirect: vpre)')
.get('/vpre/ReSharper.Nuke.svg')
.expectRedirect('/resharper/v/ReSharper.Nuke.svg?include_prereleases')
.get('/vpre/StyleCop.StyleCop.svg')
.expectRedirect('/resharper/v/StyleCop.StyleCop.svg?include_prereleases')

View File

@@ -1,5 +1,6 @@
import Joi from 'joi'
import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
import { pathParams } from '../index.js'
import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
@@ -38,19 +39,39 @@ class ScrutinizerBuild extends ScrutinizerBuildBase {
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
static examples = [
{
title: 'Scrutinizer build (GitHub/Bitbucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
static openApi = {
'/scrutinizer/build/{vcs}/{user}/{repo}': {
get: {
summary: 'Scrutinizer build (GitHub/Bitbucket)',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
),
},
staticPreview: renderBuildStatusBadge({ status: 'passing' }),
},
]
'/scrutinizer/build/{vcs}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer build (GitHub/Bitbucket) with branch',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
@@ -71,19 +92,29 @@ class ScrutinizerGitLabBuild extends ScrutinizerBuildBase {
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
static examples = [
{
title: 'Scrutinizer build (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
static openApi = {
'/scrutinizer/build/gl/{instance}/{user}/{repo}': {
get: {
summary: 'Scrutinizer build (GitLab)',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
),
},
staticPreview: renderBuildStatusBadge({ status: 'passing' }),
},
]
'/scrutinizer/build/gl/{instance}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer build (GitLab) with branch',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import { colorScale } from '../color-formatters.js'
import { NotFound } from '../index.js'
import { NotFound, pathParams } from '../index.js'
import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
@@ -70,19 +70,39 @@ class ScrutinizerCoverage extends ScrutinizerCoverageBase {
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
static examples = [
{
title: 'Scrutinizer coverage (GitHub/BitBucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
static openApi = {
'/scrutinizer/coverage/{vcs}/{user}/{repo}': {
get: {
summary: 'Scrutinizer coverage (GitHub/Bitbucket)',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
),
},
staticPreview: this.render({ coverage: 86 }),
},
]
'/scrutinizer/coverage/{vcs}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer coverage (GitHub/Bitbucket) with branch',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
@@ -103,19 +123,29 @@ class ScrutinizerCoverageGitLab extends ScrutinizerCoverageBase {
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
static examples = [
{
title: 'Scrutinizer coverage (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
static openApi = {
'/scrutinizer/coverage/gl/{instance}/{user}/{repo}': {
get: {
summary: 'Scrutinizer coverage (GitLab)',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
),
},
staticPreview: this.render({ coverage: 94 }),
},
]
'/scrutinizer/coverage/gl/{instance}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer coverage (GitLab) with branch',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({

View File

@@ -1,5 +1,6 @@
import Joi from 'joi'
import { colorScale } from '../color-formatters.js'
import { pathParams } from '../index.js'
import ScrutinizerBase from './scrutinizer-base.js'
const schema = Joi.object({
@@ -58,19 +59,39 @@ class ScrutinizerQuality extends ScrutinizerQualityBase {
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
static examples = [
{
title: 'Scrutinizer code quality (GitHub/Bitbucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
static openApi = {
'/scrutinizer/quality/{vcs}/{user}/{repo}': {
get: {
summary: 'Scrutinizer quality (GitHub/Bitbucket)',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
),
},
staticPreview: this.render({ score: 8.26 }),
},
]
'/scrutinizer/quality/{vcs}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer quality (GitHub/Bitbucket) with branch',
parameters: pathParams(
{
name: 'vcs',
example: 'g',
description: 'Platform: Either Github or Bitbucket',
schema: { type: 'string', enum: this.getEnum('vcs') },
},
{ name: 'user', example: 'filp' },
{ name: 'repo', example: 'whoops' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
@@ -91,19 +112,29 @@ class ScrutinizerQualityGitLab extends ScrutinizerQualityBase {
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
static examples = [
{
title: 'Scrutinizer coverage (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
static openApi = {
'/scrutinizer/quality/gl/{instance}/{user}/{repo}': {
get: {
summary: 'Scrutinizer quality (GitLab)',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
),
},
staticPreview: this.render({ score: 10.0 }),
},
]
'/scrutinizer/quality/gl/{instance}/{user}/{repo}/{branch}': {
get: {
summary: 'Scrutinizer quality (GitLab) with branch',
parameters: pathParams(
{ name: 'instance', example: 'propertywindow' },
{ name: 'user', example: 'propertywindow' },
{ name: 'repo', example: 'client' },
{ name: 'branch', example: 'master' },
),
},
},
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({

View File

@@ -2,7 +2,7 @@ import Joi from 'joi'
import dayjs from 'dayjs'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
total: nonNegativeInteger,
@@ -32,36 +32,40 @@ export default class SourceforgeDownloads extends BaseJsonService {
static route = {
base: 'sourceforge',
pattern: ':interval(dt|dm|dw|dd)/:project/:folder*',
pattern: ':interval(dd|dw|dm|dt)/:project/:folder*',
}
static examples = [
{
title: 'SourceForge Downloads',
pattern: ':interval(dt|dm|dw|dd)/:project',
namedParams: {
interval: 'dm',
project: 'sevenzip',
static openApi = {
'/sourceforge/{interval}/{project}': {
get: {
summary: 'SourceForge Downloads',
parameters: pathParams(
{
name: 'interval',
example: 'dm',
description: 'Daily, Weekly, Monthly, or Total downloads',
schema: { type: 'string', enum: this.getEnum('interval') },
},
{ name: 'project', example: 'sevenzip' },
),
},
staticPreview: this.render({
downloads: 215990,
interval: 'dm',
}),
},
{
title: 'SourceForge Downloads (folder)',
pattern: ':interval(dt|dm|dw|dd)/:project/:folder',
namedParams: {
interval: 'dm',
project: 'arianne',
folder: 'stendhal',
'/sourceforge/{interval}/{project}/{folder}': {
get: {
summary: 'SourceForge Downloads (folder)',
parameters: pathParams(
{
name: 'interval',
example: 'dm',
description: 'Daily, Weekly, Monthly, or Total downloads',
schema: { type: 'string', enum: this.getEnum('interval') },
},
{ name: 'project', example: 'arianne' },
{ name: 'folder', example: 'stendhal' },
),
},
staticPreview: this.render({
downloads: 550,
interval: 'dm',
}),
},
]
}
static defaultBadgeData = { label: 'sourceforge' }

Some files were not shown because too many files have changed in this diff Show More