Compare commits

..

93 Commits

Author SHA1 Message Date
github-actions[bot]
72e9322f29 Changelog for Release server-2025-01-01 (#10781)
* Update Changelog

* Update CHANGELOG.md

---------

Co-authored-by: release[bot] <actions@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2025-01-01 10:26:16 +00:00
Danny Yang
bf91e268d6 Add [PypiTypes] badge (#10774)
* add python typing badge

* prettier

* Update services/pypi/pypi-typing.service.js

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>

* address comments

* rename

* fix test

---------

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>
2024-12-31 16:33:36 +00:00
LitoMore
85b44b9152 feat(endpoint-badge): add logoSize support (#10132) 2024-12-31 15:45:39 +00:00
chris48s
c567f6cde4 Migrate to ESLint 9 (#10762)
* update ESLint related packages

* migrate to flat config format

* Fix prefer-const error

Fixes
'overrideLogoSize' is never reassigned. Use 'const' instead

* remove irrelevant eslint-disable comment

These comments came from a swizzled upstream
component but never did anything in our codebase.

ESLint 9 does not allow disable comments
for rules that are not registered.

* remove irrelevant eslint-disable comments

These were here because in the past we were applying
mocha lint rules to files which contained no tests

ESLint 9 now flags eslint-disable comments
that aren't doing anythings

* remove irrelevant eslint-disable comment

ESLint 9 now flags eslint-disable comments
that aren't doing anything

* there are no .tsx files in our code any more

* include .mjs files in linting and formatting

* update sort-class-members rule for openApi property

and update the handful of files violating it
2024-12-31 13:54:25 +00:00
chris48s
7108e08670 use isDecimalPercentage in tests for github/lab top lang (#10752) 2024-12-31 13:25:38 +00:00
Pierre-Yves Bigourdan
b4c21fd65d Fix [JiraSprint] tests (#10746)
* Fix [JiraSprint] tests

* Switch to https URLs
2024-12-30 16:38:02 +00:00
LitoMore
00c73c872d fix auto-sized logo sizes (#10764) 2024-12-29 17:04:24 +00:00
LitoMore
e6b66a8865 fix parseDate() test for different timezones (#10765) 2024-12-29 16:50:17 +00:00
jNullj
56b9c78b65 docs: add blog post for Simple Icons 14 upgrade (#10773)
* docs: add blog post for Simple Icons 14 upgrade

* fix: simpleicons release renames

some of the removed icons are added in the release notes as new with a new name, these are marked as renamed with this commit.

* Update frontend/blog/2024-12-27-simpleicons14.md

---------

Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2024-12-29 16:44:40 +00:00
Aravind Putrevu
cb309028db Add [Coderabbit] PR Stats service and tests (#10749)
* Add Coderabbit PR Stats service and tests

- Implemented a new service `CoderabbitStats` to fetch and display pull request statistics from the CodeRabbit API.
- Created a corresponding tester file to validate the service's functionality, including tests for valid repositories, repository not found, and server errors.
- The service returns a badge with the number of PRs and appropriate error messages based on the API response.

This addition enhances the analysis capabilities of the application by integrating CodeRabbit statistics.

* Refactor CoderabbitStats service to track reviews instead of PRs

- Updated the service to fetch and display the number of reviews from the CodeRabbit API, changing the schema and badge labels accordingly.
- Modified the tester file to reflect the new endpoint and expected responses, including regex for message validation.
- Enhanced error handling in the service to return more descriptive error messages for invalid repositories and server errors.

This change improves the accuracy of the statistics provided by the service, aligning it with the intended functionality of tracking reviews.

* Enhance CodeRabbitStats service and tests

- Updated the CodeRabbitStats service to include OpenAPI documentation and improved error handling for repository not found scenarios.
- Changed badge label from 'CodeRabbit' to 'CodeRabbit Reviews' for clarity.
- Modified the tester file to reflect the new badge format and error messages, ensuring consistency with the service updates.
- Adjusted regex patterns for message validation in tests.

These changes improve the usability and accuracy of the CodeRabbit statistics service.

* Update services/coderabbit/coderabbit-stats.tester.js

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>

* Addressed codereview comments, changed tests.

* Fix label casing in CodeRabbitStats service and tests

* Update services/coderabbit/coderabbit-stats.service.js

Co-authored-by: chris48s <chris48s@users.noreply.github.com>

* Update CodeRabbitStats service and tests to improve error messaging

- Changed example section in CodeRabbitStats service from 'github, gitlab, bitbucket' to 'github' as per review comment.
- Updated error message for 404 response to 'provider or repo not found', to reflect the right code.

* Added github, gitlab, bitbucket to provider.

* Changing names to match the service name, removing unneeded test.

---------

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2024-12-29 16:35:31 +00:00
dependabot[bot]
7174c5ad17 chore(deps-dev): bump @typescript-eslint/parser from 8.18.1 to 8.18.2 (#10770)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.18.1 to 8.18.2.
- [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/v8.18.2/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-12-29 16:27:12 +00:00
dependabot[bot]
8f8eff5e2f chore(deps-dev): bump eslint-plugin-react from 7.37.2 to 7.37.3 (#10767)
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.37.2 to 7.37.3.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.2...v7.37.3)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  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-12-29 16:26:50 +00:00
dependabot[bot]
d4090a2665 chore(deps-dev): bump concurrently from 9.1.0 to 9.1.1 (#10768)
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 9.1.0 to 9.1.1.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v9.1.0...v9.1.1)

---
updated-dependencies:
- dependency-name: concurrently
  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-12-27 16:07:45 +00:00
dependabot[bot]
53eab46666 chore(deps): bump chalk from 5.4.0 to 5.4.1 (#10769)
Bumps [chalk](https://github.com/chalk/chalk) from 5.4.0 to 5.4.1.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/compare/v5.4.0...v5.4.1)

---
updated-dependencies:
- dependency-name: chalk
  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-12-27 16:00:02 +00:00
dependabot[bot]
2b90459bb4 chore(deps): bump simple-icons from 13.21.0 to 14.0.0 (#10771)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.21.0 to 14.0.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.21.0...14.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>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-27 13:54:02 +00:00
Jeevan Joshi
0cbc1319ce add [PUB] downloads badge (#10745)
* add PUB downloads badge

* test PUB downloads badge

* add PUB monthly downloads badge

* test PUB monthly downloads badge

* revert color override
2024-12-23 09:47:17 +00:00
dependabot[bot]
ac01fdefcc chore(deps-dev): bump cypress from 13.16.1 to 13.17.0 (#10755)
Bumps [cypress](https://github.com/cypress-io/cypress) from 13.16.1 to 13.17.0.
- [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.16.1...v13.17.0)

---
updated-dependencies:
- dependency-name: cypress
  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-12-20 19:34:22 +00:00
dependabot[bot]
3d6c438df2 chore(deps): bump @sentry/node from 8.45.0 to 8.47.0 (#10756)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.45.0 to 8.47.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/8.47.0/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/8.45.0...8.47.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-12-20 19:34:01 +00:00
dependabot[bot]
a729e65393 chore(deps-dev): bump @typescript-eslint/parser from 8.18.0 to 8.18.1 (#10758)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.18.0 to 8.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/v8.18.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-12-20 19:33:38 +00:00
dependabot[bot]
c4c0c2c6f6 chore(deps): bump fast-xml-parser from 4.5.0 to 4.5.1 (#10754)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.5.0 to 4.5.1.
- [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.5.0...v4.5.1)

---
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-12-20 16:15:27 +00:00
dependabot[bot]
e4eb6cb6c4 chore(deps): bump chalk from 5.3.0 to 5.4.0 (#10759)
Bumps [chalk](https://github.com/chalk/chalk) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/chalk/chalk/releases)
- [Commits](https://github.com/chalk/chalk/compare/v5.3.0...v5.4.0)

---
updated-dependencies:
- dependency-name: chalk
  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-12-20 15:15:18 +00:00
dependabot[bot]
c4b6b7923b chore(deps): bump graphql from 16.9.0 to 16.10.0 (#10761)
Bumps [graphql](https://github.com/graphql/graphql-js) from 16.9.0 to 16.10.0.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v16.9.0...v16.10.0)

---
updated-dependencies:
- dependency-name: graphql
  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-12-20 14:58:24 +00:00
dependabot[bot]
d1058bc73d chore(deps): bump simple-icons from 13.20.0 to 13.21.0 (#10757)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.20.0 to 13.21.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.20.0...13.21.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-12-20 12:48:33 +00:00
Brijesh Kumar
f643515ee1 Add [GitLab] Top Language Badge (#10750)
* Add: GitLab Top Languages Badge

* Fix: Doc heading

* Add: Test cases + Rename service

* Update: Test URL

* Update: Requested Changes
2024-12-20 10:49:46 +00:00
chris48s
21a059d9a8 provide a non-repository scoped version of [githubcodesearch] (#10733)
and redirect /search/user/repo/q
to /search?query=q%20repo:user/repo

Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com>
2024-12-15 20:16:19 +00:00
dependabot[bot]
7c067fdcbf chore(deps): bump simple-icons from 13.19.0 to 13.20.0 (#10737)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.19.0 to 13.20.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.19.0...13.20.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-12-13 17:16:54 +00:00
dependabot[bot]
f6fde2b78b chore(deps-dev): bump prism-react-renderer from 2.4.0 to 2.4.1 (#10735)
Bumps [prism-react-renderer](https://github.com/FormidableLabs/prism-react-renderer) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/FormidableLabs/prism-react-renderer/releases)
- [Commits](https://github.com/FormidableLabs/prism-react-renderer/compare/prism-react-renderer@2.4.0...prism-react-renderer@2.4.1)

---
updated-dependencies:
- dependency-name: prism-react-renderer
  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-12-13 17:16:20 +00:00
dependabot[bot]
1da19cffaa chore(deps-dev): bump nodemon from 3.1.7 to 3.1.9 (#10734)
Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.7 to 3.1.9.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v3.1.7...v3.1.9)

---
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-12-13 17:15:11 +00:00
dependabot[bot]
e1d0ab10ae chore(deps-dev): bump c8 from 10.1.2 to 10.1.3 (#10738)
Bumps [c8](https://github.com/bcoe/c8) from 10.1.2 to 10.1.3.
- [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/v10.1.2...v10.1.3)

---
updated-dependencies:
- dependency-name: c8
  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-12-13 17:14:56 +00:00
dependabot[bot]
e3f382114e chore(deps-dev): bump start-server-and-test from 2.0.8 to 2.0.9 (#10740)
Bumps [start-server-and-test](https://github.com/bahmutov/start-server-and-test) from 2.0.8 to 2.0.9.
- [Release notes](https://github.com/bahmutov/start-server-and-test/releases)
- [Commits](https://github.com/bahmutov/start-server-and-test/compare/v2.0.8...v2.0.9)

---
updated-dependencies:
- dependency-name: start-server-and-test
  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-12-13 17:14:40 +00:00
dependabot[bot]
ab16aa3c03 chore(deps): bump @sentry/node from 8.42.0 to 8.45.0 (#10736)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.42.0 to 8.45.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/8.42.0...8.45.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-12-13 17:03:41 +00:00
dependabot[bot]
deaf85f6fc chore(deps-dev): bump node-mocks-http from 1.16.1 to 1.16.2 (#10741)
Bumps [node-mocks-http](https://github.com/eugef/node-mocks-http) from 1.16.1 to 1.16.2.
- [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/v1.16.1...v1.16.2)

---
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-12-13 17:02:44 +00:00
dependabot[bot]
d481322a3f chore(deps-dev): bump eslint-plugin-jsdoc from 50.6.0 to 50.6.1 (#10739)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 50.6.0 to 50.6.1.
- [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/v50.6.0...v50.6.1)

---
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-12-13 17:02:20 +00:00
dependabot[bot]
71cddb7abc chore(deps-dev): bump lint-staged from 15.2.10 to 15.2.11 (#10743)
Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.10 to 15.2.11.
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.10...v15.2.11)

---
updated-dependencies:
- dependency-name: lint-staged
  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-12-13 17:01:43 +00:00
dependabot[bot]
1756cb834e chore(deps-dev): bump @typescript-eslint/parser from 8.17.0 to 8.18.0 (#10742)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.17.0 to 8.18.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/v8.18.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-12-13 17:01:25 +00:00
chris48s
fb816ecf93 Upgrade to Chai 5.x (#10732)
* remove chai-string plugin

* upgrade chai and sinon-chai

* update chai imports

* migrate badge-maker tests to ESM
2024-12-11 19:28:45 +00:00
Hervé Boutemy
075f1b450e [ReproducibleCentral] add Reproducible Central in Dependencies (#10705)
* add Reproducible Central in Dependencies

* first updates based on feedback

* use new artifact-level json data = map version -> RB result

* fix unit tests

* improve code with provided snippets: thank you

* add description
2024-12-11 08:07:39 +00:00
dependabot[bot]
d7bb13c0bc chore(deps-dev): bump cypress from 13.16.0 to 13.16.1 (#10723)
Bumps [cypress](https://github.com/cypress-io/cypress) from 13.16.0 to 13.16.1.
- [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.16.0...v13.16.1)

---
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-12-07 11:46:44 +00:00
dependabot[bot]
f79ed50c9b chore(deps-dev): bump mocha from 10.8.2 to 11.0.1 (#10725)
Bumps [mocha](https://github.com/mochajs/mocha) from 10.8.2 to 11.0.1.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v10.8.2...v11.0.1)

---
updated-dependencies:
- dependency-name: mocha
  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-12-07 11:46:25 +00:00
dependabot[bot]
0d08f2ef50 chore(deps): bump @sentry/node from 8.41.0 to 8.42.0 (#10730)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.41.0 to 8.42.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/8.41.0...8.42.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-12-07 11:45:38 +00:00
dependabot[bot]
a87e1c32fb chore(deps): bump byte-size from 9.0.0 to 9.0.1 (#10722)
Bumps [byte-size](https://github.com/75lb/byte-size) from 9.0.0 to 9.0.1.
- [Release notes](https://github.com/75lb/byte-size/releases)
- [Commits](https://github.com/75lb/byte-size/compare/v9.0.0...v9.0.1)

---
updated-dependencies:
- dependency-name: byte-size
  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-12-07 11:22:38 +00:00
dependabot[bot]
f48a205032 chore(deps-dev): bump @typescript-eslint/parser from 8.16.0 to 8.17.0 (#10729)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.16.0 to 8.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/v8.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-12-06 18:15:12 +00:00
dependabot[bot]
6b6cc3ac70 chore(deps): bump @xmldom/xmldom from 0.9.5 to 0.9.6 (#10728)
Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.9.5 to 0.9.6.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.9.5...0.9.6)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  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-12-06 17:53:13 +00:00
dependabot[bot]
ab7aaff60e chore(deps-dev): bump prettier from 3.4.1 to 3.4.2 (#10719)
Bumps [prettier](https://github.com/prettier/prettier) from 3.4.1 to 3.4.2.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.4.1...3.4.2)

---
updated-dependencies:
- dependency-name: prettier
  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-12-06 17:28:08 +00:00
dependabot[bot]
e5dbfe7ea5 chore(deps): bump simple-icons from 13.18.0 to 13.19.0 (#10726)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.18.0 to 13.19.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.18.0...13.19.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-12-06 14:55:54 +00:00
chris48s
bed8a63d76 add a privacy policy (#10715)
* add a privacy policy

* formatting fix
2024-12-02 18:21:42 +00:00
chris48s
151c70dd17 Add ability to format bytes as metric or IEC; affects [bundlejs bundlephobia ChromeWebStoreSize CratesSize DockerSize GithubRepoSize GithubCodeSize GithubSize NpmUnpackedSize SpigetDownloadSize steam VisualStudioAppCenterReleasesSize whatpulse] (#10547)
* add renderSizeBadge helper, use it everywhere

- switch from pretty-bytes to byte-size
- add renderSizeBadge() helper function
- match upstream conventions for metric/IEC units
- add new test helpers and use them in service tests

* unrelated: fix npm unpacked size query param schema

not strictly related to this PR
but I noticed it was broken

* chromewebstore: reformat size string, test against isIecFileSize
2024-12-01 19:53:26 +00:00
chris48s
b7d7f4545d enable auto suspend/resume (#10699) 2024-12-01 19:37:19 +00:00
github-actions[bot]
2a20f813df Changelog for Release server-2024-12-01 (#10716)
* 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-12-01 12:39:41 +00:00
dependabot[bot]
3b465533fd chore(deps): bump simple-icons from 13.17.0 to 13.18.0 (#10712)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.17.0 to 13.18.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.17.0...13.18.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-11-29 13:44:01 +00:00
dependabot[bot]
df719ea2db chore(deps-dev): bump @easyops-cn/docusaurus-search-local (#10711)
Bumps [@easyops-cn/docusaurus-search-local](https://github.com/easyops-cn/docusaurus-search-local/tree/HEAD/packages/docusaurus-search-local) from 0.45.0 to 0.46.1.
- [Release notes](https://github.com/easyops-cn/docusaurus-search-local/releases)
- [Commits](https://github.com/easyops-cn/docusaurus-search-local/commits/v0.46.1/packages/docusaurus-search-local)

---
updated-dependencies:
- dependency-name: "@easyops-cn/docusaurus-search-local"
  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-11-29 13:43:57 +00:00
dependabot[bot]
9ee8ee8cbf chore(deps): bump got from 14.4.4 to 14.4.5 (#10713)
Bumps [got](https://github.com/sindresorhus/got) from 14.4.4 to 14.4.5.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v14.4.4...v14.4.5)

---
updated-dependencies:
- dependency-name: got
  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-11-29 13:32:40 +00:00
dependabot[bot]
def3007602 chore(deps): bump @sentry/node from 8.40.0 to 8.41.0 (#10710)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.40.0 to 8.41.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/8.40.0...8.41.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-11-29 13:32:19 +00:00
dependabot[bot]
7d7f70b4b5 chore(deps-dev): bump @typescript-eslint/parser from 8.15.0 to 8.16.0 (#10709)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.15.0 to 8.16.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/v8.16.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-11-29 13:30:50 +00:00
dependabot[bot]
2ded4aa7b6 chore(deps-dev): bump prettier from 3.3.3 to 3.4.1 (#10714)
Bumps [prettier](https://github.com/prettier/prettier) from 3.3.3 to 3.4.1.
- [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.3.3...3.4.1)

---
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-11-29 13:30:25 +00:00
dependabot[bot]
da2745d523 chore(deps-dev): bump chai-as-promised from 8.0.0 to 8.0.1 (#10707)
Bumps [chai-as-promised](https://github.com/chaijs/chai-as-promised) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/chaijs/chai-as-promised/releases)
- [Commits](https://github.com/chaijs/chai-as-promised/compare/v8.0.0...v8.0.1)

---
updated-dependencies:
- dependency-name: chai-as-promised
  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-11-29 13:30:02 +00:00
dependabot[bot]
1338bf3192 chore(deps-dev): bump eslint-plugin-jsdoc from 50.5.0 to 50.6.0 (#10706)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 50.5.0 to 50.6.0.
- [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/v50.5.0...v50.6.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  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-11-29 13:29:37 +00:00
jNullj
e8e253d21e remove obsolete [CratesSize] test for null size (#10688)
* remove obsolete test for version without size

changes upstream removed all null crate_size
see also rust-lang/crates.io#9926

* remove null handling in CratesSize service
2024-11-26 21:01:29 +00:00
jNullj
60aa530966 fix [DockerVersion] service test (#10690)
* Update docker version tester to check Python instead of Memcached

Memcached tags don't follow semver, might fail depending on which  tag was last added.
Python seems to follow semver for all tags.
fixes #10689

* Update docker version tester to check docker-dev instead of python

its a depricated image and is not expected to change tags
tag is semver

* Update docker version tester to check example-voting-app-vote

deprecated yet by docker, i think we can count on that to be stable
2024-11-26 20:33:46 +00:00
dependabot[bot]
9447077c08 chore(deps): bump @sentry/node from 8.38.0 to 8.40.0 (#10691)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.38.0 to 8.40.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/8.38.0...8.40.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-11-22 17:23:11 +00:00
dependabot[bot]
b60d738999 chore(deps-dev): bump cypress from 13.15.2 to 13.16.0 (#10695)
Bumps [cypress](https://github.com/cypress-io/cypress) from 13.15.2 to 13.16.0.
- [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.15.2...v13.16.0)

---
updated-dependencies:
- dependency-name: cypress
  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-11-22 16:59:48 +00:00
dependabot[bot]
85fb206c8b chore(deps-dev): bump @typescript-eslint/parser from 8.14.0 to 8.15.0 (#10696)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.14.0 to 8.15.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/v8.15.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-11-22 16:38:42 +00:00
dependabot[bot]
d1210e2311 chore(deps): bump jsonpath-plus from 10.1.0 to 10.2.0 (#10693)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) from 10.1.0 to 10.2.0.
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v10.1.0...v10.2.0)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  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-11-22 16:13:16 +00:00
dependabot[bot]
85dd5a599f chore(deps): bump qs from 6.13.0 to 6.13.1 (#10698)
Bumps [qs](https://github.com/ljharb/qs) from 6.13.0 to 6.13.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.13.1)

---
updated-dependencies:
- dependency-name: qs
  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-11-22 15:36:50 +00:00
dependabot[bot]
e108e40930 chore(deps): bump simple-icons from 13.16.0 to 13.17.0 (#10697)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.16.0 to 13.17.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.16.0...13.17.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-11-22 15:23:43 +00:00
dependabot[bot]
eaa4317039 chore(deps): bump smol-toml from 1.3.0 to 1.3.1 (#10694)
Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](https://github.com/squirrelchat/smol-toml/commits)

---
updated-dependencies:
- dependency-name: smol-toml
  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-11-22 14:54:08 +00:00
chris48s
5cdef88bcc Add renderDateBadge helper; affects [aur BitbucketLastCommit chrome date eclipse factorio galaxytoolshed GiteaLastCommit GistLastCommit GithubCreatedAt GithubHacktoberfest GithubIssueDetail GithubLastCommit GithubReleaseDate GitlabLastCommit maven npm openvsx snapcraft SourceforgeLastCommit steam vaadin visualstudio wordpress] (#10682)
* add and consistently use parseDate and renderDateBadge helpers

also move

- age
- formatDate
- formatRelativeDate

to date.js

* fix bug in wordpress last update badge

* validate in formatDate() and age()

it is going to be unlikely we'll invoke either of these
directly now, but lets calidate here too

* remove unusued imports

* reverse colours for galaxy toolshed
2024-11-17 13:15:28 +00:00
chris48s
4132ca2e7e Add blog post about token pool, improve 'authorise our app' CTA (#10683)
* Add blog post about token pool, improve 'authorise our app' CTA

* simplify first sentence
2024-11-16 16:50:04 +00:00
dependabot[bot]
e1541acc11 chore(deps): bump @renovatebot/ruby-semver from 3.0.23 to 4.0.0 (#10677)
Bumps [@renovatebot/ruby-semver](https://github.com/renovatebot/ruby-semver) from 3.0.23 to 4.0.0.
- [Release notes](https://github.com/renovatebot/ruby-semver/releases)
- [Changelog](https://github.com/renovatebot/ruby-semver/blob/main/.releaserc.json)
- [Commits](https://github.com/renovatebot/ruby-semver/compare/3.0.23...4.0.0)

---
updated-dependencies:
- dependency-name: "@renovatebot/ruby-semver"
  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-11-15 21:26:36 +00:00
dependabot[bot]
f5dd749ae0 chore(deps-dev): bump eslint-plugin-jsdoc from 50.4.3 to 50.5.0 (#10680)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 50.4.3 to 50.5.0.
- [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/v50.4.3...v50.5.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  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-11-15 21:11:38 +00:00
dependabot[bot]
ef17850f7e chore(deps-dev): bump @typescript-eslint/parser from 8.13.0 to 8.14.0 (#10679)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.13.0 to 8.14.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/v8.14.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-11-15 21:11:20 +00:00
dependabot[bot]
5bde4266c8 chore(deps): bump node-pg-migrate from 7.7.1 to 7.8.0 (#10676)
Bumps [node-pg-migrate](https://github.com/salsita/node-pg-migrate) from 7.7.1 to 7.8.0.
- [Release notes](https://github.com/salsita/node-pg-migrate/releases)
- [Changelog](https://github.com/salsita/node-pg-migrate/blob/main/CHANGELOG.md)
- [Commits](https://github.com/salsita/node-pg-migrate/compare/v7.7.1...v7.8.0)

---
updated-dependencies:
- dependency-name: node-pg-migrate
  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-11-15 21:10:57 +00:00
dependabot[bot]
e57edb42bd chore(deps): bump @sentry/node from 8.37.1 to 8.38.0 (#10674)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.37.1 to 8.38.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/8.37.1...8.38.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-11-15 21:10:29 +00:00
dependabot[bot]
69f9251e1c chore(deps-dev): bump nock from 13.5.5 to 13.5.6 (#10675)
Bumps [nock](https://github.com/nock/nock) from 13.5.5 to 13.5.6.
- [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.5.5...v13.5.6)

---
updated-dependencies:
- dependency-name: nock
  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-11-15 21:09:36 +00:00
dependabot[bot]
39d3fd332b chore(deps): bump @renovatebot/pep440 from 3.0.20 to 4.0.1 (#10678)
Bumps [@renovatebot/pep440](https://github.com/renovatebot/pep440) from 3.0.20 to 4.0.1.
- [Release notes](https://github.com/renovatebot/pep440/releases)
- [Changelog](https://github.com/renovatebot/pep440/blob/main/.releaserc.json)
- [Commits](https://github.com/renovatebot/pep440/compare/3.0.20...4.0.1)

---
updated-dependencies:
- dependency-name: "@renovatebot/pep440"
  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-11-15 19:36:10 +00:00
chris48s
58f5b99fea remove 'operations' section from README (#10673) 2024-11-14 20:20:34 +00:00
chris48s
20959b15db add jNullj, remove inactive maintainers (#10672) 2024-11-14 18:21:04 +00:00
chris48s
cbb7ab5e8b reduce overhead of NPM Last Update badge; test [npm] (#10666)
* reduce overhead of [NpmLastUpdate] badge

* use buildRoute for version without tag
2024-11-13 19:02:48 +00:00
chris48s
2bd926e65f rename postfix param to suffix (#10667) 2024-11-11 19:34:08 +00:00
jNullj
04638ab0ee Refactor - use renderVersionBadge - part 4 [githubrelease githubtag] (#10656)
* feat: add forcePrerelease option to renderVersionBadge function

Sometimes API would indicate if a version is pre-release while the version number does not have to be semantically a prerelease like in github-release service.
We don't use a isPrerelease that can also force a non-preleases as we trust here developer semantic over API tagging.

* refactor: GithubRelease to use renderVersionBadge

* refactor: GithubTag use renderVersionBadge

* refactor: change forcePrerelease to isPrerelease
2024-11-11 19:16:14 +00:00
jNullj
4d203e1937 fix [githubpipenv] service tests (#10658)
* fix failing test 'Locked version of VCS dependency'

replaced tested package which is missing with another example

* Add regex validator for commit hash format

* fix vcs dependency test to use pipenv's pypiserver
2024-11-11 19:15:35 +00:00
dependabot[bot]
6219c6da82 chore(deps-dev): bump cypress from 13.15.1 to 13.15.2 (#10661)
Bumps [cypress](https://github.com/cypress-io/cypress) from 13.15.1 to 13.15.2.
- [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.15.1...v13.15.2)

---
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-11-10 13:42:45 +00:00
dependabot[bot]
a16cf24b52 chore(deps): bump simple-icons from 13.15.0 to 13.16.0 (#10660)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 13.15.0 to 13.16.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/13.15.0...13.16.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-11-10 13:36:52 +00:00
dependabot[bot]
d3a1ef2ff7 chore(deps): bump @sentry/node from 8.36.0 to 8.37.1 (#10665)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 8.36.0 to 8.37.1.
- [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/8.36.0...8.37.1)

---
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-11-10 13:28:43 +00:00
dependabot[bot]
32b1e341d7 chore(deps-dev): bump @typescript-eslint/parser from 8.12.2 to 8.13.0 (#10664)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.12.2 to 8.13.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/v8.13.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-11-10 13:28:23 +00:00
dependabot[bot]
2d5b72b207 chore(deps-dev): bump concurrently from 9.0.1 to 9.1.0 (#10663)
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 9.0.1 to 9.1.0.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v9.0.1...v9.1.0)

---
updated-dependencies:
- dependency-name: concurrently
  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-11-10 13:28:08 +00:00
dependabot[bot]
0c4fed4dc6 chore(deps): bump got from 14.4.3 to 14.4.4 (#10662)
Bumps [got](https://github.com/sindresorhus/got) from 14.4.3 to 14.4.4.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v14.4.3...v14.4.4)

---
updated-dependencies:
- dependency-name: got
  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-11-10 13:27:48 +00:00
jNullj
8af909d118 fix [githubmanifest] service tests (#10657)
* Change badge label from 'version' to 'manifest' in GitHub manifest tests

* revert version label for failed paths
2024-11-10 13:09:27 +00:00
anatawa12
00d72da97e add [WingetVersion] Badge (#10245)
* feat: add winget version badge

* chore: accept dotted path instead of slashed

* test: add test for winget-version

* fix: remove debug code

* chore: use winget-specific version compare algorithm

* fix: support latest and unknown

* fix(winget/version): trailing '.0' handling is incorrect

* fix(winget/version): latest returns last newest version instead of the first newest version

* fix(winget/version): confusing subpackage and version name

* fix(winget/version): example for latest is incorrect

* add a couple of extra test cases for latest()

---------

Co-authored-by: chris48s <git@chris-shaw.dev>
2024-11-04 19:05:32 +00:00
usr3
4ec62fa445 Fix broken URL for pingpong.one (#10655)
Fix broken URL for https://pingpong.one for uptime status monitoring.
2024-11-04 11:40:03 +00:00
Pierre-Yves Bigourdan
57520a974f Remove [Nuget MyGet] color tests (#10654) 2024-11-02 18:34:25 +00:00
Ambati Mohan Kumar
8c7872a666 [npm] - Last update badge added (#10641)
* Added npm last update badge

* extended NpmBase class instead of BaseJsonService.

* added scoped packages to last update.

* introduced additionalQueryParamSchema

this is to add other query params schema, other than the one present in NpmBase.

* removed version query param

* in absence of modified date, it'll fetch created.

* removed version query param.

* added dist-tags.

* Update services/npm/npm-last-update.service.js

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>

* refactored handle method for dist-tags.

* Update services/npm/npm-last-update.service.js

Co-authored-by: chris48s <chris48s@users.noreply.github.com>

* added date validation check.

* added date validation check.

* added date validation check.

---------

Co-authored-by: jNullj <15849761+jNullj@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2024-11-02 17:37:07 +00:00
Pierre-Yves Bigourdan
ad82f7647a Add YouTube-specific privacy notes (#10646) 2024-11-02 11:20:57 +00:00
131 changed files with 5247 additions and 2816 deletions

View File

@@ -1,7 +0,0 @@
/api-docs/
/build
/coverage
/__snapshots__
public
badge-maker/node_modules/
!.github/

View File

@@ -1,189 +0,0 @@
extends:
- standard
- standard-jsx
- standard-react
- prettier
- eslint:recommended
globals:
JSX: 'readonly'
parserOptions:
# Override eslint-config-standard, which incorrectly sets this to "module",
# though that setting is only for ES6 modules, not CommonJS modules.
sourceType: 'script'
settings:
react:
version: '16.8'
jsdoc:
mode: typescript
plugins:
- chai-friendly
- jsdoc
- mocha
- icedfrisby
- no-extension-in-require
- sort-class-members
- import
- react-hooks
- promise
overrides:
# For simplicity's sake, when possible prefer to add rules to the top-level
# list of rules, even if they only apply to certain files. That way the
# rules listed here are only ones which conflict.
- files:
- 'badge-maker/**/*.js'
- '**/*.cjs'
env:
node: true
es6: true
- files:
- '**/*.js'
- '!frontend/**/*.js'
- '!badge-maker/**/*.js'
env:
node: true
es6: true
parserOptions:
sourceType: 'module'
parser: '@typescript-eslint/parser'
rules:
no-console: 'off'
- files:
- '**/*.ts'
parserOptions:
sourceType: 'module'
parser: '@typescript-eslint/parser'
- files:
- 'frontend/**/*.js'
parserOptions:
sourceType: 'module'
env:
browser: true
rules:
import/extensions:
['error', 'never', { 'json': 'always', 'yml': 'always' }]
- files:
- 'core/base-service/**/*.js'
- 'services/**/*.js'
rules:
sort-class-members/sort-class-members:
[
'error',
{
order:
[
'name',
'category',
'isDeprecated',
'route',
'auth',
'examples',
'_cacheLength',
'defaultBadgeData',
'render',
'constructor',
'fetch',
'transform',
'handle',
],
},
]
- files:
- '**/*.spec.@(js|ts|tsx)'
- '**/*.integration.js'
- '**/test-helpers.js'
- 'core/service-test-runner/**/*.js'
env:
mocha: true
rules:
mocha/no-exclusive-tests: 'error'
mocha/no-skipped-tests: 'error'
mocha/no-mocha-arrows: 'error'
mocha/prefer-arrow-callback: 'error'
- files:
- 'services/**/*.tester.js'
rules:
icedfrisby/no-exclusive-tests: 'error'
icedfrisby/no-skipped-tests: 'error'
rules:
# Disable some rules from eslint:recommended.
no-empty: ['error', { 'allowEmptyCatch': true }]
no-use-before-define: 'off'
# These should be disabled by eslint-config-prettier, but are not.
no-extra-semi: 'off'
# Shields additions.
no-var: 'error'
prefer-const: 'error'
arrow-body-style: ['error', 'as-needed']
no-extension-in-require/main: 'error'
object-shorthand: ['error', 'properties']
prefer-template: 'error'
promise/prefer-await-to-then: 'error'
func-style: ['error', 'declaration', { 'allowArrowFunctions': true }]
new-cap: ['error', { 'capIsNew': true }]
import/order: ['error', { 'newlines-between': 'never' }]
quotes:
['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }]
# Account for destructuring responses from upstream services,
# many of which do not follow camelcase
# Based on original rule configuration from eslint-config-standard
camelcase:
[
'error',
{
ignoreDestructuring: true,
properties: 'never',
ignoreGlobals: true,
allow: ['^UNSAFE_'],
},
]
# Chai friendly.
no-unused-expressions: 'off'
chai-friendly/no-unused-expressions: 'error'
# jsdoc plugin:
# don't require every class/function to have a docblock
jsdoc/require-jsdoc: 'off'
# allow Joi as an undefined type
jsdoc/no-undefined-types: ['error', { definedTypes: ['Joi'] }]
# all the other recommended rules as errors (not warnings)
jsdoc/check-alignment: 'error'
jsdoc/check-param-names: 'error'
jsdoc/check-tag-names: 'error'
jsdoc/check-types: 'error'
jsdoc/implements-on-classes: 'error'
jsdoc/tag-lines: ['error', 'any', { 'startLines': 1 }]
jsdoc/require-param: 'error'
jsdoc/require-param-description: 'error'
jsdoc/require-param-name: 'error'
jsdoc/require-param-type: 'error'
jsdoc/require-returns: 'error'
jsdoc/require-returns-check: 'error'
jsdoc/require-returns-description: 'error'
jsdoc/require-returns-type: 'error'
jsdoc/valid-types: 'error'
react/prop-types: 'off'
react/jsx-sort-props: 'error'
react-hooks/rules-of-hooks: 'error'
react-hooks/exhaustive-deps: 'error'
jsx-quotes: ['error', 'prefer-double']

View File

@@ -4,6 +4,28 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2025-01-01
- Add [PypiTypes] badge [#10774](https://github.com/badges/shields/issues/10774)
- feat(endpoint-badge): add logoSize support [#10132](https://github.com/badges/shields/issues/10132)
- fix auto-sized logo sizes [#10764](https://github.com/badges/shields/issues/10764)
- Add [Coderabbit] PR Stats service and tests [#10749](https://github.com/badges/shields/issues/10749)
- add [PUB] downloads badge [#10745](https://github.com/badges/shields/issues/10745)
- Add [GitLab] Top Language Badge [#10750](https://github.com/badges/shields/issues/10750)
- provide a non-repository scoped version of [githubcodesearch] [#10733](https://github.com/badges/shields/issues/10733)
- [ReproducibleCentral] add Reproducible Central in Dependencies [#10705](https://github.com/badges/shields/issues/10705)
- Add ability to format bytes as metric or IEC; affects [bundlejs bundlephobia ChromeWebStoreSize CratesSize DockerSize GithubRepoSize GithubCodeSize GithubSize NpmUnpackedSize SpigetDownloadSize steam VisualStudioAppCenterReleasesSize whatpulse] [#10547](https://github.com/badges/shields/issues/10547)
- Dependency updates
## server-2024-12-01
- add [WingetVersion] Badge [#10245](https://github.com/badges/shields/issues/10245)
- Fix broken URL for pingpong.one [#10655](https://github.com/badges/shields/issues/10655)
- [npm] - Last update badge added [#10641](https://github.com/badges/shields/issues/10641)
- reduce overhead of NPM Last Update badge; test [npm] [#10666](https://github.com/badges/shields/issues/10666)
- Add YouTube-specific privacy notes [#10646](https://github.com/badges/shields/issues/10646)
- Dependency updates
## server-2024-11-02
- cleanly handle null or undefined result from jsonpath-plus [#10645](https://github.com/badges/shields/issues/10645)

View File

@@ -198,25 +198,19 @@ You can read more about [the project's inception][thread],
Maintainers:
- [calebcartwright](https://github.com/calebcartwright) (core team)
- [chris48s](https://github.com/chris48s) (core team)
- [Daniel15](https://github.com/Daniel15) (core team)
- [paulmelnikow](https://github.com/paulmelnikow) (core team)
- [platan](https://github.com/platan) (core team)
- [PyvesB](https://github.com/PyvesB) (core team)
- [RedSparr0w](https://github.com/RedSparr0w) (core team)
Operations:
- [calebcartwright](https://github.com/calebcartwright)
- [chris48s](https://github.com/chris48s)
- [jNullj](https://github.com/jnullj)
- [paulmelnikow](https://github.com/paulmelnikow)
- [PyvesB](https://github.com/PyvesB)
Alumni:
- [Daniel15](https://github.com/Daniel15)
- [espadrine](https://github.com/espadrine)
- [olivierlacan](https://github.com/olivierlacan)
- [platan](https://github.com/platan)
- [RedSparr0w](https://github.com/RedSparr0w)
## License

View File

@@ -1,9 +1,8 @@
'use strict'
const path = require('path')
const { spawn } = require('child-process-promise')
const { expect, use } = require('chai')
use(require('chai-string'))
import path from 'path'
import { spawn } from 'child-process-promise'
import { expect, use } from 'chai'
use(require('sinon-chai'))
function runCli(args) {
@@ -15,7 +14,7 @@ function runCli(args) {
describe('The CLI', function () {
it('should provide a help message', async function () {
const { stdout } = await runCli([])
expect(stdout).to.startWith('Usage')
expect(stdout.startsWith('Usage')).to.be.true
})
it('should produce default badges', async function () {

View File

@@ -1,7 +1,7 @@
'use strict'
const { expect } = require('chai')
const { makeBadge, ValidationError } = require('.')
import { expect } from 'chai'
import { makeBadge, ValidationError } from '.'
describe('makeBadge function', function () {
it('should produce badge with valid input', async function () {

View File

@@ -1,10 +1,10 @@
'use strict'
const { test, given, forCases } = require('sazerac')
const { expect } = require('chai')
const snapshot = require('snap-shot-it')
const prettier = require('prettier')
const makeBadge = require('./make-badge')
import { test, given, forCases } from 'sazerac'
import { expect } from 'chai'
import snapshot from 'snap-shot-it'
import prettier from 'prettier'
import makeBadge from './make-badge'
async function expectBadgeToMatchSnapshot(format) {
snapshot(await prettier.format(makeBadge(format), { parser: 'html' }))

View File

@@ -65,6 +65,7 @@ const serviceDataSchema = Joi.object({
namedLogo: Joi.string(),
logoSvg: Joi.string(),
logoColor: optionalStringWhenNamedLogoPresent,
logoSize: optionalStringWhenNamedLogoPresent,
logoWidth: optionalNumberWhenAnyLogoPresent,
cacheSeconds: Joi.number().integer().min(0),
style: Joi.string(),

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import chai from 'chai'
import { expect, use } from 'chai'
import sinon from 'sinon'
import prometheus from 'prom-client'
import chaiAsPromised from 'chai-as-promised'
@@ -16,8 +16,7 @@ import {
import BaseService from './base.js'
import { MetricHelper, MetricNames } from './metric-helper.js'
import '../register-chai-plugins.spec.js'
const { expect } = chai
chai.use(chaiAsPromised)
use(chaiAsPromised)
const queryParamSchema = Joi.object({
queryParamA: Joi.string(),

View File

@@ -1,5 +1,5 @@
import { test, given } from 'sazerac'
import chai, { expect } from 'chai'
import { expect, use } from 'chai'
import sinon from 'sinon'
import httpMocks from 'node-mocks-http'
import chaiDatetime from 'chai-datetime'
@@ -10,7 +10,7 @@ import {
setCacheHeadersForStaticResource,
serverHasBeenUpSinceResourceCached,
} from './cache-headers.js'
chai.use(chaiDatetime)
use(chaiDatetime)
describe('Cache header functions', function () {
let res

View File

@@ -47,13 +47,13 @@ export default function coalesceBadge(
label: overrideLabel,
logo: overrideLogo,
logoColor: overrideLogoColor,
logoSize: overrideLogoSize,
link: overrideLink,
colorB: legacyOverrideColor,
colorA: legacyOverrideLabelColor,
} = overrides
let {
logoWidth: overrideLogoWidth,
logoSize: overrideLogoSize,
color: overrideColor,
labelColor: overrideLabelColor,
} = overrides

View File

@@ -17,8 +17,14 @@ describe('mergeQueries function', function () {
}
`),
),
).to.equalIgnoreSpaces(
'query ($param: String!) { foo(param: $param) { bar } }',
).to.equal(
print(gql`
query ($param: String!) {
foo(param: $param) {
bar
}
}
`),
)
expect(
@@ -38,8 +44,15 @@ describe('mergeQueries function', function () {
`,
),
),
).to.equalIgnoreSpaces(
'query ($param: String!) { foo(param: $param) { bar } baz }',
).to.equal(
print(gql`
query ($param: String!) {
foo(param: $param) {
bar
}
baz
}
`),
)
expect(
@@ -62,7 +75,15 @@ describe('mergeQueries function', function () {
`,
),
),
).to.equalIgnoreSpaces('{ foo bar baz }')
).to.equal(
print(gql`
{
foo
bar
baz
}
`),
)
expect(
print(
@@ -79,7 +100,14 @@ describe('mergeQueries function', function () {
`,
),
),
).to.equalIgnoreSpaces('{ foo bar }')
).to.equal(
print(gql`
{
foo
bar
}
`),
)
})
it('throws an error when passed invalid params', function () {

View File

@@ -115,7 +115,6 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
const result = handlerOptions.handler(
filteredQueryParams,
match,
// eslint-disable-next-line mocha/prefer-arrow-callback
function sendBadge(format, badgeData) {
if (serverUnresponsive) {
return
@@ -128,7 +127,6 @@ function handleRequest(cacheHeaderConfig, handlerOptions) {
makeSend(format, ask.res, end)(svg)
},
)
// eslint-disable-next-line promise/prefer-await-to-then
if (result && result.catch) {
// eslint-disable-next-line promise/prefer-await-to-then
result.catch(err => {

View File

@@ -1,15 +1,14 @@
import path from 'path'
import { fileURLToPath } from 'url'
import chai from 'chai'
import { expect, use } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import {
loadServiceClasses,
getServicePaths,
InvalidService,
} from './loader.js'
chai.use(chaiAsPromised)
use(chaiAsPromised)
const { expect } = chai
const fixturesDir = path.join(
path.dirname(fileURLToPath(import.meta.url)),
'loader-test-fixtures',

View File

@@ -1,4 +1,4 @@
import chai from 'chai'
import { expect } from 'chai'
import {
category2openapi,
pathParam,
@@ -7,7 +7,6 @@ import {
queryParams,
} from './openapi.js'
import BaseJsonService from './base-json.js'
const { expect } = chai
class OpenApiService extends BaseJsonService {
static category = 'build'

View File

@@ -1,5 +1,3 @@
import { use } from 'chai'
import chaiString from 'chai-string'
import sinonChai from 'sinon-chai'
use(chaiString)
use(sinonChai)

View File

@@ -83,7 +83,7 @@ class ServiceTester {
.before(() => {
this.beforeEach()
})
// eslint-disable-next-line mocha/prefer-arrow-callback, promise/prefer-await-to-then
// eslint-disable-next-line promise/prefer-await-to-then
.finally(function () {
// `this` is the IcedFrisby instance.
let responseBody

249
eslint.config.js Normal file
View File

@@ -0,0 +1,249 @@
import chaiFriendlyPlugin from 'eslint-plugin-chai-friendly'
import cypressPlugin from 'eslint-plugin-cypress/flat'
import jsdocPlugin from 'eslint-plugin-jsdoc'
import mochaPlugin from 'eslint-plugin-mocha'
import icedfrisbyPlugin from 'eslint-plugin-icedfrisby'
import sortClassMembersPlugin from 'eslint-plugin-sort-class-members'
import importPlugin from 'eslint-plugin-import'
import reactHooksPlugin from 'eslint-plugin-react-hooks'
import prettierConfig from 'eslint-plugin-prettier/recommended'
import promisePlugin from 'eslint-plugin-promise'
import globals from 'globals'
import neostandard from 'neostandard'
import tsParser from '@typescript-eslint/parser'
import js from '@eslint/js'
// Config that is used across the whole codebase
// and customisations to built-in ESLint rules
const globalConfig = {
plugins: {
import: importPlugin,
promise: promisePlugin,
},
rules: {
'import/order': ['error', { 'newlines-between': 'never' }],
'promise/prefer-await-to-then': 'error',
// ESLint built-in rules config
'no-empty': ['error', { allowEmptyCatch: true }],
'no-var': 'error',
'prefer-const': 'error',
'arrow-body-style': ['error', 'as-needed'],
'object-shorthand': ['error', 'properties'],
'prefer-template': 'error',
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
'new-cap': ['error', { capIsNew: true }],
quotes: [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: false },
],
camelcase: [
'error',
{
ignoreDestructuring: true,
properties: 'never',
ignoreGlobals: true,
allow: ['^UNSAFE_'],
},
],
},
}
// config specific to linting Node (CommonJS) files
const commonJsConfig = {
files: ['badge-maker/**/*.js', '**/*.cjs'],
languageOptions: {
globals: {
...globals.node,
},
},
}
// config specific to linting Node (ESModules) files
const nodeEsmConfig = {
files: ['**/*.@(js|mjs)', '!frontend/**/*.js', '!badge-maker/**/*.js'],
languageOptions: {
globals: {
...globals.node,
},
parser: tsParser,
sourceType: 'module',
},
rules: {
'no-console': 'off',
},
}
// config specific to linting Frontend (ESModules) files
const frontendConfig = {
files: ['frontend/**/*.js'],
plugins: {
'react-hooks': reactHooksPlugin,
},
languageOptions: {
globals: {
...globals.browser,
},
sourceType: 'module',
},
rules: {
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'error',
},
}
// config specific to linting Services
const servicesConfig = {
files: ['core/base-service/**/*.js', 'services/**/*.js'],
plugins: {
'sort-class-members': sortClassMembersPlugin,
},
rules: {
'sort-class-members/sort-class-members': [
'error',
{
order: [
'name',
'category',
'isDeprecated',
'route',
'auth',
'openApi',
'_cacheLength',
'defaultBadgeData',
'render',
'constructor',
'fetch',
'transform',
'handle',
],
},
],
},
}
// config specific to linting Mocha tests
const mochaConfig = {
files: [
'**/*.spec.@(js|mjs|ts)',
'**/*.integration.js',
'**/test-helpers.js',
'core/service-test-runner/**/*.js',
],
plugins: {
mocha: mochaPlugin,
},
languageOptions: {
globals: {
...globals.mocha,
},
},
rules: {
'mocha/no-exclusive-tests': 'error',
'mocha/no-skipped-tests': 'error',
'mocha/no-mocha-arrows': 'error',
'mocha/prefer-arrow-callback': 'error',
'no-unused-expressions': 'off',
},
}
// config specific to linting Cypress tests
const cypressConfig = {
files: ['**/*.cy.@(js|ts)'],
...cypressPlugin.configs.recommended,
}
// append these to cypress.configs.recommended, without overwriting
cypressConfig.plugins.mocha = mochaPlugin
cypressConfig.rules['mocha/no-exclusive-tests'] = 'error'
cypressConfig.rules['mocha/no-skipped-tests'] = 'error'
cypressConfig.rules['mocha/no-mocha-arrows'] = 'off'
// config specific to linting Service tests (IcedFrisby)
const serviceTestsConfig = {
files: ['services/**/*.tester.js'],
plugins: {
icedfrisby: icedfrisbyPlugin,
},
rules: {
'icedfrisby/no-exclusive-tests': 'error',
'icedfrisby/no-skipped-tests': 'error',
'no-unused-expressions': 'off',
},
}
// config specific to linting JSDoc comments
const jsDocConfig = {
plugins: {
jsdoc: jsdocPlugin,
},
rules: {
'jsdoc/require-jsdoc': 'off',
'jsdoc/no-undefined-types': ['error', { definedTypes: ['Joi'] }],
'jsdoc/check-alignment': 'error',
'jsdoc/check-param-names': 'error',
'jsdoc/check-tag-names': 'error',
'jsdoc/check-types': 'error',
'jsdoc/implements-on-classes': 'error',
'jsdoc/tag-lines': ['error', 'any', { startLines: 1 }],
'jsdoc/require-param': 'error',
'jsdoc/require-param-description': 'error',
'jsdoc/require-param-name': 'error',
'jsdoc/require-param-type': 'error',
'jsdoc/require-returns': 'error',
'jsdoc/require-returns-check': 'error',
'jsdoc/require-returns-description': 'error',
'jsdoc/require-returns-type': 'error',
'jsdoc/valid-types': 'error',
},
}
const config = [
{
ignores: [
'api-docs/',
'build',
'coverage',
'__snapshots__',
'public',
'badge-maker/node_modules/',
'!.github/',
'frontend/.docusaurus/**',
],
},
js.configs.recommended,
chaiFriendlyPlugin.configs.recommendedFlat,
...neostandard({ noStyle: true }),
globalConfig,
commonJsConfig,
nodeEsmConfig,
frontendConfig,
servicesConfig,
mochaConfig,
cypressConfig,
serviceTestsConfig,
jsDocConfig,
// register prettierConfig last, as per
// https://github.com/prettier/eslint-plugin-prettier?tab=readme-ov-file#configuration-new-eslintconfigjs
prettierConfig,
]
export default config

View File

@@ -25,6 +25,8 @@ processes = []
processes = ["app"]
protocol = "tcp"
script_checks = []
auto_stop_machines = "suspend"
auto_start_machines = true
[services.concurrency]
hard_limit = 25

View File

@@ -0,0 +1,28 @@
---
slug: token-pool
title: How shields.io uses the GitHub API
authors:
name: chris48s
title: Shields.io Core Team
url: https://github.com/chris48s
image_url: https://avatars.githubusercontent.com/u/6025893
tags: []
---
We serve a lot of badges which display information fetched from the GitHub API. When I say a lot, this varies a bit but in a typical hour we make hundreds of thousands of calls to the GitHub API.
But hang on. GitHub's API has rate limits.
Specifically, users can make up to [5,000 requests per hour](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-authenticated-users) to GitHub's v3/REST API. The v4/GraphQL also applies rate limits, but it is based on a slightly more complicated [points-based system](https://docs.github.com/en/graphql/overview/rate-limits-and-node-limits-for-the-graphql-api#primary-rate-limit).
In any case, we are clearly making many times more requests to GitHub's API than would be allowed with a single token.
So how are we doing that? Well, we have lots of tokens. To elaborate on that slightly, as a user of shields.io you can choose to share a token with us to help increase our rate limit. Here's how it works:
- Authorize our [OAuth Application](https://img.shields.io/github-auth).
- This shares with us a GitHub token which has read-only access to public data. We only ask for the minimum permissions necessary. Authorizing the OAuth app doesn't allow us access to your private data or allow us to perform any actions on your behalf.
- Your token is added to a pool of tokens shared by other users like you.
- When we need to make a request to the GitHub API, we pick one of the tokens from our pool. We only make a handful of requests with each token before picking another from the pool.
- If you ever decide you would not like to continue sharing a token with us, you can revoke the Shields.io OAuth app at https://github.com/settings/applications. You can do this at any time. This will de-activate the token you have shared with us and we'll remove it from the pool.
This method allows us (with your help) to make hundreds of thousands of request per hour to the GitHub API. Because we have thousands of tokens in the pool and we only make a few requests with each one before picking another token from the pool, most users don't notice any meaningful impact on their available rate limit as a result of authorizing our app.

View File

@@ -0,0 +1,81 @@
---
slug: simple-icons-14
title: Simple Icons 14
authors:
name: jNullj
title: Shields.io Core Team
url: https://github.com/jNullj
image_url: https://avatars.githubusercontent.com/u/15849761
tags: []
---
Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 14. This release removes 53 icons and renames 6:
Renames:
- D3.js to D3
- Tencent QQ to QQ
- T-Mobile to Deutsche Telekom
- Nuxt.js to Nuxt
- smash.gg start.gg
- Tutanota to Tuta
Removals:
- Adobe
- Adobe Acrobat Reader
- Adobe After Effects
- Adobe Audition
- Adobe Creative Cloud
- Adobe Dreamweaver
- Adobe Fonts
- Adobe Illustrator
- Adobe InDesign
- Adobe Lightroom
- Adobe Lightroom Classic
- Adobe Photoshop
- Adobe Premiere Pro
- Adobe XD
- ASKfm
- Caffeine
- CKEditor 4
- Cliqz
- Coil
- del.icio.us
- El Jueves
- Ello
- FeatHub
- Fluxus
- Foursquare City Guide
- Funimation
- Game & Watch
- Géant
- Katacoda
- LinkedIn
- Magento
- Marketo
- Microgenetics
- Nintendo
- Nintendo 3DS
- Nintendo DS
- Nintendo GameCube
- Nintendo Switch
- Oracle
- Pokémon
- RadioPublic
- Realm
- Revue
- Skyrock
- Spinrilla
- StackPath
- Stitcher
- Studyverse
- Tableau
- Uptobox
- Wii
- Wii U
- Zerply
More detail can be found in the [release notes](https://github.com/simple-icons/simple-icons/releases/tag/14.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.

View File

@@ -123,6 +123,15 @@ const config = {
},
],
},
{
title: 'Policy',
items: [
{
label: 'Privacy Policy',
href: '/privacy',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Shields.io. Built with Docusaurus.`,
},

View File

@@ -0,0 +1,50 @@
# Privacy Policy
Shields.io is non-tracking and privacy-respecting. This Privacy Policy explains how we handle your data in compliance with the General Data Protection Regulation (GDPR).
## 1. Hosting and Service Providers
We use [fly.io](https://fly.io) for hosting and [CloudFlare](https://www.cloudflare.com) for DNS and CDN services. These third-party providers process requests to deliver and secure our website. Please refer to their privacy policies for more information:
- https://fly.io/legal/privacy-policy/
- https://www.cloudflare.com/en-gb/privacypolicy/
## 2. Cookies
We do not use any cookies on our website.
## 3. Logs and Data Collection
We do not store any logs of your visits, requests, or other activities on our site.
## 4. Error Reporting
If a request fails, we send an error report to [Sentry](https://sentry.io/), our error-tracking service.
These reports contain technical data about the error but do not include any personally identifiable information (PII), such as your IP address. For details on Sentry's data processing, refer to their privacy policy:
- https://sentry.io/privacy/
## 5. GitHub OAuth App
Users may optionally authorize our [GitHub OAuth app](https://img.shields.io/github-auth).
Authorizing our app shares with us a GitHub token which has read-only access to public data. We only ask for the minimum permissions necessary. Authorizing the OAuth app doesn't allow us access to your private data or allow us to perform any actions on your behalf.
The only information we store is the **GitHub token** and the **timestamp** when you authorized the app.
- The GitHub token is used solely to increase the rate limit for accessing the GitHub API.
- The signup timestamp is stored for internal record-keeping purposes.
We don't collect or store any other information like your username or email address.
If you decide you would not like to continue sharing a token with us, you can revoke the Shields.io OAuth app at https://github.com/settings/applications. You can do this at any time. This will de-activate the token you have shared with us and we'll remove it from our token pool.
## 6. Your Rights
Under the GDPR, users have rights concerning their personal data, including access, correction, deletion, and objection to processing.
Since we process minimal data, these rights are not relevant to most users of the service.
## 7. Contact Us
If you have questions about this Privacy Policy or our data practices, you can contact us at team at shields.io

View File

@@ -261,7 +261,6 @@ function Curl({ postman, codeSamples }) {
{tokens.map((line, i) => (
// this <span> does have a key but eslint fails
// to detect it because it is an arg to getLineProps()
// eslint-disable-next-line react/jsx-key
<span
{...getLineProps({
line,
@@ -276,7 +275,6 @@ function Curl({ postman, codeSamples }) {
return (
// this <span> does have a key but eslint fails
// to detect it because it is an arg to getLineProps()
// eslint-disable-next-line react/jsx-key
<span
{...getTokenProps({
token,

View File

@@ -93,10 +93,13 @@ function getSimpleIcon({ name, color, style, size }) {
if (size === 'auto') {
const { width: iconWidth, height: iconHeight } = getIconSize(key)
if (iconWidth > iconHeight) {
if (iconWidth !== iconHeight) {
const path = resetIconPosition(simpleIcons[key].path)
iconSvg = iconSvg
.replace('viewBox="0 0 24 24"', `viewBox="0 0 24 ${iconHeight}"`)
.replace(
'viewBox="0 0 24 24"',
`viewBox="0 0 ${iconWidth} ${iconHeight}"`,
)
.replace(/<path d=".*"\/>/, `<path d="${path}"/>`)
}
}

View File

@@ -1,5 +1,3 @@
/* eslint-disable camelcase */
exports.shorthands = undefined
exports.up = pgm => {

4141
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,15 +21,16 @@
"url": "https://github.com/badges/shields"
},
"dependencies": {
"@renovatebot/pep440": "^3.0.20",
"@renovatebot/ruby-semver": "^3.0.23",
"@sentry/node": "^8.36.0",
"@renovatebot/pep440": "^4.0.1",
"@renovatebot/ruby-semver": "^4.0.0",
"@sentry/node": "^8.47.0",
"@shields_io/camp": "^18.1.2",
"@xmldom/xmldom": "0.9.5",
"@xmldom/xmldom": "0.9.6",
"badge-maker": "file:badge-maker",
"byte-size": "^9.0.1",
"bytes": "^3.1.2",
"camelcase": "^8.0.0",
"chalk": "^5.3.0",
"chalk": "^5.4.1",
"check-node-version": "^4.2.1",
"cloudflare-middleware": "^1.0.4",
"config": "^3.3.12",
@@ -38,33 +39,32 @@
"decamelize": "^3.2.0",
"emojic": "^1.1.17",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^4.5.0",
"fast-xml-parser": "^4.5.1",
"glob": "^11.0.0",
"global-agent": "^3.0.0",
"got": "^14.4.3",
"graphql": "16.9.0",
"got": "^14.4.5",
"graphql": "16.10.0",
"graphql-tag": "^2.12.6",
"joi": "17.13.3",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
"jsonpath-plus": "^10.1.0",
"jsonpath-plus": "^10.2.0",
"lodash.countby": "^4.6.0",
"lodash.groupby": "^4.6.0",
"lodash.times": "^4.3.2",
"matcher": "^5.0.0",
"node-env-flag": "^0.1.0",
"node-pg-migrate": "^7.7.1",
"node-pg-migrate": "^7.8.0",
"parse-link-header": "^2.0.0",
"path-to-regexp": "^6.3.0",
"pg": "^8.13.1",
"pretty-bytes": "^6.1.1",
"priorityqueuejs": "^2.0.0",
"prom-client": "^15.1.3",
"qs": "^6.13.0",
"qs": "^6.13.1",
"query-string": "^9.1.1",
"semver": "~7.6.3",
"simple-icons": "13.15.0",
"smol-toml": "1.3.0",
"simple-icons": "14.0.0",
"smol-toml": "1.3.1",
"svg-path-bbox": "^2.1.0",
"svgpath": "^2.6.0",
"webextension-store-meta": "^1.2.4",
@@ -82,12 +82,12 @@
"coverage:report:generate": "c8 report",
"coverage:report:open": "open-cli coverage/lcov-report/index.html",
"coverage:report": "run-s --silent coverage:report:generate coverage:report:open",
"lint": "eslint \"**/*.@(cjs|js|ts|tsx)\"",
"prettier": "prettier --write \"**/*.@(cjs|js|ts|tsx|md|json|yml)\"",
"prettier:check": "prettier --check \"**/*.@(cjs|js|ts|tsx|md|json|yml)\"",
"lint": "eslint \"**/*.@(cjs|mjs|js|ts)\"",
"prettier": "prettier --write \"**/*.@(cjs|mjs|js|ts|md|json|yml)\"",
"prettier:check": "prettier --check \"**/*.@(cjs|mjs|js|ts|md|json|yml)\"",
"danger": "danger",
"test:e2e": "cypress run",
"test:core": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.spec.js\" \"lib/**/*.spec.js\" \"services/**/*.spec.js\"",
"test:core": "cross-env TZ='UTC' NODE_CONFIG_ENV=test mocha \"core/**/*.spec.js\" \"lib/**/*.spec.js\" \"services/**/*.spec.js\"",
"test:package": "mocha \"badge-maker/**/*.spec.js\"",
"test:entrypoint": "cross-env NODE_CONFIG_ENV=test mocha entrypoint.spec.js",
"test:integration": "cross-env NODE_CONFIG_ENV=test mocha \"core/**/*.integration.js\" \"services/**/*.integration.js\"",
@@ -124,7 +124,7 @@
"docusaurus:clear": "docusaurus clear frontend"
},
"lint-staged": {
"**/*.@(js|ts|tsx)": [
"**/*.@(cjs|mjs|js|ts)": [
"eslint --fix",
"prettier --write"
],
@@ -148,60 +148,56 @@
"devDependencies": {
"@docusaurus/core": "^3.5.2",
"@docusaurus/preset-classic": "^3.5.2",
"@easyops-cn/docusaurus-search-local": "^0.45.0",
"@easyops-cn/docusaurus-search-local": "^0.46.1",
"@mdx-js/react": "^3.1.0",
"@typescript-eslint/parser": "^8.12.2",
"c8": "^10.1.2",
"@typescript-eslint/parser": "^8.18.2",
"c8": "^10.1.3",
"caller": "^1.1.0",
"chai": "^4.5.0",
"chai-as-promised": "^8.0.0",
"chai": "5.1.2",
"chai-as-promised": "^8.0.1",
"chai-datetime": "^1.8.1",
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clsx": "^2.1.1",
"concurrently": "^9.0.1",
"cypress": "^13.15.1",
"concurrently": "^9.1.1",
"cypress": "^13.17.0",
"cypress-wait-for-stable-dom": "^0.1.0",
"danger": "^12.3.3",
"deepmerge": "^4.3.1",
"docusaurus-preset-openapi": "0.7.5",
"eslint": "8.57.1",
"eslint": "9.17.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "17.1.0",
"eslint-config-standard-jsx": "11.0.0",
"eslint-config-standard-react": "13.0.0",
"eslint-plugin-chai-friendly": "^1.0.1",
"eslint-plugin-cypress": "^3.6.0",
"eslint-plugin-icedfrisby": "^0.1.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsdoc": "^50.4.3",
"eslint-plugin-mocha": "^10.5.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "6.6.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-sort-class-members": "^1.21.0",
"eslint-plugin-chai-friendly": "1.0.1",
"eslint-plugin-cypress": "4.1.0",
"eslint-plugin-icedfrisby": "0.2.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "50.6.1",
"eslint-plugin-mocha": "10.5.0",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-react-hooks": "5.1.0",
"eslint-plugin-sort-class-members": "1.21.0",
"form-data": "^4.0.1",
"globals": "15.14.0",
"icedfrisby": "4.0.0",
"icedfrisby-nock": "^2.1.0",
"is-svg": "^5.1.0",
"jsdoc": "^4.0.4",
"lint-staged": "^15.2.10",
"lint-staged": "^15.2.11",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.8",
"mocha": "^10.8.2",
"mocha": "^11.0.1",
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.2.1",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.5.5",
"node-mocks-http": "^1.16.1",
"nodemon": "^3.1.7",
"neostandard": "0.12.0",
"nock": "13.5.6",
"node-mocks-http": "^1.16.2",
"nodemon": "^3.1.9",
"npm-run-all": "^4.1.5",
"open-cli": "^8.0.0",
"portfinder": "^1.0.32",
"prettier": "3.3.3",
"prism-react-renderer": "^2.4.0",
"prettier": "3.4.2",
"prism-react-renderer": "^2.4.1",
"react": "^18.3.0",
"react-dom": "^18.3.1",
"read-all-stdin-sync": "^1.0.5",
@@ -209,9 +205,9 @@
"sazerac": "^2.0.0",
"simple-git-hooks": "^2.11.1",
"sinon": "^19.0.2",
"sinon-chai": "^3.7.0",
"sinon-chai": "4.0.0",
"snap-shot-it": "^7.9.10",
"start-server-and-test": "2.0.8",
"start-server-and-test": "2.0.9",
"tsd": "^0.31.2",
"url": "^0.11.4"
},

View File

@@ -1,10 +1,8 @@
import Joi from 'joi'
import {
floorCount as floorCountColor,
age as ageColor,
} from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { floorCount as floorCountColor } from '../color-formatters.js'
import { renderLicenseBadge } from '../licenses.js'
import { metric, formatDate } from '../text-formatters.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import {
BaseJsonService,
@@ -243,16 +241,10 @@ class AurLastModified extends BaseAurService {
static defaultBadgeData = { label: 'last modified' }
static render({ date }) {
const color = ageColor(date)
const message = formatDate(date)
return { color, message }
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
const date = 1000 * parseInt(json.results[0].LastModified)
return this.constructor.render({ date })
return renderDateBadge(date)
}
}

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { BaseJsonService, NotFound, pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { relativeUri } from '../validators.js'
const schema = Joi.object({
@@ -43,13 +42,6 @@ export default class BitbucketLastCommit extends BaseJsonService {
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ user, repo, branch, path }) {
// https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commits/#api-repositories-workspace-repo-slug-commits-get
return this._requestJson({
@@ -76,6 +68,6 @@ export default class BitbucketLastCommit extends BaseJsonService {
if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
return this.constructor.render({ commitDate: commit.date })
return renderDateBadge(commit.date)
}
}

View File

@@ -1,9 +1,11 @@
import Joi from 'joi'
import { BaseJsonService, pathParam, queryParam } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
const schema = Joi.object({
size: Joi.object({
compressedSize: Joi.string().required(),
rawCompressedSize: nonNegativeInteger,
}).required(),
}).required()
@@ -76,13 +78,6 @@ export default class BundlejsPackage extends BaseJsonService {
static defaultBadgeData = { label: 'bundlejs', color: 'informational' }
static render({ size }) {
return {
label: 'minified size (gzip)',
message: size,
}
}
async fetch({ scope, packageName, exports }) {
const searchParams = {
q: `${scope ? `${scope}/` : ''}${packageName}`,
@@ -110,7 +105,7 @@ export default class BundlejsPackage extends BaseJsonService {
async handle({ scope, packageName }, { exports }) {
const json = await this.fetch({ scope, packageName, exports })
const size = json.size.compressedSize
return this.constructor.render({ size })
const size = json.size.rawCompressedSize
return renderSizeBadge(size, 'metric', 'minified size (gzip)')
}
}

View File

@@ -1,26 +1,26 @@
import { isFileSize } from '../test-validators.js'
import { isMetricFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('bundlejs/package (packageName)')
.get('/jquery.json')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
t.create('bundlejs/package (version)')
.get('/react@18.2.0.json')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
t.create('bundlejs/package (scoped)')
.get('/@cycle/rx-run.json')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
t.create('bundlejs/package (select exports)')
.get('/value-enhancer.json?exports=isVal,val')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
t.create('bundlejs/package (scoped version select exports)')
.get('/@ngneat/falso@6.4.0.json?exports=randEmail,randFullName')
.expectBadge({ label: 'minified size (gzip)', message: isFileSize })
.expectBadge({ label: 'minified size (gzip)', message: isMetricFileSize })
t.create('bundlejs/package (not found)')
.get('/react@18.2.0.json')

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, pathParams } from '../index.js'
@@ -112,10 +112,7 @@ export default class Bundlephobia extends BaseJsonService {
static render({ format, size }) {
const label = format === 'min' ? 'minified size' : 'minzipped size'
return {
label,
message: prettyBytes(size),
}
return renderSizeBadge(size, 'iec', label)
}
async fetch({ scope, packageName, version }) {

View File

@@ -1,4 +1,4 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
@@ -13,42 +13,42 @@ const data = [
{
format: formats.A,
get: '/min/preact.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.B,
get: '/min/preact/8.0.0.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.C,
get: '/min/@cycle/core.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.D,
get: '/min/@cycle/core/7.0.0.json',
expect: { label: 'minified size', message: isFileSize },
expect: { label: 'minified size', message: isIecFileSize },
},
{
format: formats.A,
get: '/minzip/preact.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.B,
get: '/minzip/preact/8.0.0.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.C,
get: '/minzip/@cycle/core.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.D,
get: '/minzip/@cycle/core/7.0.0.json',
expect: { label: 'minzipped size', message: isFileSize },
expect: { label: 'minzipped size', message: isIecFileSize },
},
{
format: formats.A,

View File

@@ -1,5 +1,4 @@
import { age } from '../color-formatters.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { NotFound, pathParams } from '../index.js'
import BaseChromeWebStoreService from './chrome-web-store-base.js'
@@ -31,11 +30,6 @@ export default class ChromeWebStoreLastUpdated extends BaseChromeWebStoreService
throw new NotFound({ prettyMessage: 'not found' })
}
const lastUpdatedDate = Date.parse(lastUpdated)
return {
message: formatDate(lastUpdatedDate),
color: age(lastUpdatedDate),
}
return renderDateBadge(lastUpdated)
}
}

View File

@@ -1,4 +1,4 @@
import { NotFound, pathParams } from '../index.js'
import { InvalidResponse, NotFound, pathParams } from '../index.js'
import BaseChromeWebStoreService from './chrome-web-store-base.js'
export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
@@ -22,6 +22,17 @@ export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
color: 'blue',
}
transform(sizeStr) {
const match = sizeStr.match(/^(\d+)([a-zA-Z]+)$/)
if (!match) {
throw new InvalidResponse({
prettyMessage: 'size does not match expected format',
})
}
const [, size, units] = match
return `${size} ${units}`
}
async handle({ storeId }) {
const chromeWebStore = await this.fetch({ storeId })
const size = chromeWebStore.size()
@@ -30,6 +41,6 @@ export default class ChromeWebStoreSize extends BaseChromeWebStoreService {
throw new NotFound({ prettyMessage: 'not found' })
}
return { message: size }
return { message: this.transform(size) }
}
}

View File

@@ -1,11 +1,11 @@
import { createServiceTester } from '../tester.js'
import { isIecFileSize } from '../test-validators.js'
export const t = await createServiceTester()
const isFileSize = /^\d+(\.\d+)?(MiB|KiB)$/
t.create('Size').get('/nccfelhkfpbnefflolffkclhenplhiab.json').expectBadge({
label: 'extension size',
message: isFileSize,
message: isIecFileSize,
})
t.create('Size (not found)')

View File

@@ -0,0 +1,70 @@
import Joi from 'joi'
import { BaseJsonService, pathParams } from '../index.js'
const schema = Joi.object({
reviews: Joi.number().required(),
}).required()
class CodeRabbitPullRequest extends BaseJsonService {
static category = 'analysis'
static route = {
base: 'coderabbit',
pattern: 'prs/:provider(github|bitbucket|gitlab)/:org/:repo',
}
static openApi = {
'/coderabbit/prs/{provider}/{org}/{repo}': {
get: {
summary: 'CodeRabbit Pull Request Reviews',
description:
'This badge pulls the number of PRs reviewed by [CodeRabbit](https://coderabbit.ai), AI code review tool',
parameters: pathParams(
{
name: 'provider',
example: 'github',
description: 'Version Control Provider',
schema: { type: 'string', enum: this.getEnum('provider') },
},
{
name: 'org',
example: 'coderabbitai',
description: 'Organization or User name',
},
{
name: 'repo',
example: 'ast-grep-essentials',
description: 'Repository name',
},
),
},
},
}
static defaultBadgeData = {
label: 'coderabbit reviews',
}
static render({ reviews }) {
return {
message: `${reviews}`,
color: 'blue',
}
}
async fetch({ provider, org, repo }) {
return this._requestJson({
schema,
url: `https://api.coderabbit.ai/stats/${provider}/${org}/${repo}`,
httpErrors: {
400: 'provider or repo not found',
},
})
}
async handle({ provider, org, repo }) {
const data = await this.fetch({ provider, org, repo })
return this.constructor.render(data)
}
}
export default CodeRabbitPullRequest

View File

@@ -0,0 +1,25 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('live CodeRabbitPullRequest')
.get('/prs/github/coderabbitai/ast-grep-essentials.json')
.expectBadge({
label: 'coderabbit reviews',
message: Joi.number().min(0),
})
t.create('live CodeRabbitPullRequest nonexistent org')
.get('/prs/github/not-valid/not-found.json')
.expectBadge({
label: 'coderabbit reviews',
message: 'provider or repo not found',
})
t.create('live CodeRabbitPullRequest invalid repo')
.get('/prs/github/coderabbitai/invalid-repo-name.json')
.expectBadge({
label: 'coderabbit reviews',
message: 'provider or repo not found',
})

View File

@@ -6,7 +6,6 @@
*/
import pep440 from '@renovatebot/pep440'
import dayjs from 'dayjs'
/**
* Determines the color used for a badge based on version.
@@ -175,24 +174,7 @@ function colorScale(steps, colors, reversed) {
}
}
/**
* Determines the color used for a badge according to the age.
* Age is calculated as days elapsed till current date.
* The color varies from bright green to red as the age increases
* or the other way around if `reverse` is given `true`.
*
* @param {string} date Date string
* @param {boolean} reversed Reverse the color scale a.k.a. the older, the better
* @returns {string} Badge color
*/
function age(date, reversed = false) {
const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, !reversed)
const daysElapsed = dayjs().diff(dayjs(date), 'days')
return colorByAge(daysElapsed)
}
export {
age,
colorScale,
coveragePercentage,
downloadCount,

View File

@@ -1,7 +1,6 @@
import { expect } from 'chai'
import { forCases, given, test } from 'sazerac'
import {
age,
colorScale,
coveragePercentage,
letterScore,
@@ -53,46 +52,6 @@ describe('Color formatters', function () {
given('Z').expect('red')
})
const monthsAgo = months => {
const result = new Date()
// This looks wack but it works.
result.setMonth(result.getMonth() - months)
return result
}
test(age, () => {
given(Date.now())
.describe('when given the current timestamp')
.expect('brightgreen')
given(new Date())
.describe('when given the current Date')
.expect('brightgreen')
given(new Date(2001, 1, 1))
.describe('when given a Date many years ago')
.expect('red')
given(monthsAgo(2))
.describe('when given a Date two months ago')
.expect('yellowgreen')
given(monthsAgo(15))
.describe('when given a Date 15 months ago')
.expect('orange')
// --- reversed --- //
given(Date.now(), true)
.describe('when given the current timestamp and reversed')
.expect('red')
given(new Date(), true)
.describe('when given the current Date and reversed')
.expect('red')
given(new Date(2001, 1, 1), true)
.describe('when given a Date many years ago and reversed')
.expect('brightgreen')
given(monthsAgo(2), true)
.describe('when given a Date two months ago and reversed')
.expect('yellow')
given(monthsAgo(15), true)
.describe('when given a Date 15 months ago and reversed')
.expect('green')
})
test(version, () => {
forCases([given('1.0'), given(9), given(1.0)]).expect('blue')

View File

@@ -4,8 +4,7 @@ import { BaseJsonService, InvalidResponse } from '../index.js'
const versionSchema = Joi.object({
downloads: nonNegativeInteger,
// Crate size is not available for all versions.
crate_size: nonNegativeInteger.allow(null),
crate_size: nonNegativeInteger,
num: Joi.string().required(),
license: Joi.string().required().allow(null),
rust_version: Joi.string().allow(null),

View File

@@ -1,5 +1,5 @@
import prettyBytes from 'pretty-bytes'
import { InvalidResponse, pathParams } from '../index.js'
import { pathParams } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { BaseCratesService, description } from './crates-base.js'
export default class CratesSize extends BaseCratesService {
@@ -38,22 +38,9 @@ export default class CratesSize extends BaseCratesService {
},
}
render({ size }) {
return {
label: 'size',
message: prettyBytes(size),
color: 'blue',
}
}
async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
const size = this.constructor.getVersionObj(json).crate_size
if (size == null) {
throw new InvalidResponse({ prettyMessage: 'unknown' })
}
return this.render({ size })
return renderSizeBadge(size, 'iec')
}
}

View File

@@ -1,18 +1,14 @@
import { createServiceTester } from '../tester.js'
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
export const t = await createServiceTester()
t.create('size')
.get('/tokio.json')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })
t.create('size (with version)')
.get('/tokio/1.32.0.json')
.expectBadge({ label: 'size', message: '725 kB' })
t.create('size (with version where version doesnt have size)')
.get('/tokio/0.1.6.json')
.expectBadge({ label: 'crates.io', message: 'unknown' })
.expectBadge({ label: 'size', message: '708 KiB' })
t.create('size (not found)')
.get('/not-a-crate.json')

108
services/date.js Normal file
View File

@@ -0,0 +1,108 @@
/**
* Commonly-used functions for rendering badges containing a date
*
* @module
*/
import dayjs from 'dayjs'
import calendar from 'dayjs/plugin/calendar.js'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import { colorScale } from './color-formatters.js'
import { InvalidResponse } from './index.js'
dayjs.extend(calendar)
dayjs.extend(customParseFormat)
/**
* Parse and validate a string date into a dayjs object. Use this helper
* in preference to invoking dayjs directly when parsing a date from string.
*
* @param {...any} args - Variadic: Arguments to pass through to dayjs
* @returns {dayjs} - Parsed object
* @throws {InvalidResponse} - Error if validation fails
* @see https://day.js.org/docs/en/parse/string
* @see https://day.js.org/docs/en/parse/string-format
* @see https://day.js.org/docs/en/parse/is-valid
* @example
* parseDate('2024-01-01')
* parseDate('31/01/2024', 'DD/MM/YYYY')
* parseDate('2018 Enero 15', 'YYYY MMMM DD', 'es')
*/
function parseDate(...args) {
let date
if (args.length >= 2) {
// always use strict mode if format arg is supplied
date = dayjs(...args, true)
} else {
date = dayjs(...args)
}
if (!date.isValid()) {
throw new InvalidResponse({ prettyMessage: 'invalid date' })
}
return date
}
/**
* Returns a formatted date string without the year based on the value of input date param d.
*
* @param {Date | string | number | dayjs } d JS Date object, string, unix timestamp or dayjs object
* @returns {string} Formatted date string
*/
function formatDate(d) {
const date = parseDate(d)
const dateString = date.calendar(null, {
lastDay: '[yesterday]',
sameDay: '[today]',
lastWeek: '[last] dddd',
sameElse: 'MMMM YYYY',
})
// Trim current year from date string
return dateString.replace(` ${dayjs().year()}`, '').toLowerCase()
}
/**
* Determines the color used for a badge according to the age.
* Age is calculated as days elapsed till current date.
* The color varies from bright green to red as the age increases
* or the other way around if `reverse` is given `true`.
*
* @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
* @param {boolean} reversed Reverse the color scale (the older, the better)
* @returns {string} Badge color
*/
function age(date, reversed = false) {
const colorByAge = colorScale([7, 30, 180, 365, 730], undefined, !reversed)
const daysElapsed = dayjs().diff(parseDate(date), 'days')
return colorByAge(daysElapsed)
}
/**
* Creates a badge object that displays a date
*
* @param {Date | string | number | dayjs } date JS Date object, string, unix timestamp or dayjs object
* @param {boolean} reversed Reverse the color scale (the older, the better)
* @returns {object} A badge object that has two properties: message, and color
*/
function renderDateBadge(date, reversed = false) {
const d = parseDate(date)
const color = age(d, reversed)
const message = formatDate(d)
return { message, color }
}
/**
* Returns a relative date from the input timestamp.
* For example, day after tomorrow's timestamp will return 'in 2 days'.
*
* @param {number | string} timestamp - Unix timestamp
* @returns {string} Relative date from the unix timestamp
*/
function formatRelativeDate(timestamp) {
const parsedDate = dayjs.unix(parseInt(timestamp, 10))
if (!parsedDate.isValid()) {
return 'invalid date'
}
return dayjs().to(parsedDate).toLowerCase()
}
export { parseDate, renderDateBadge, formatDate, formatRelativeDate, age }

132
services/date.spec.js Normal file
View File

@@ -0,0 +1,132 @@
import { expect } from 'chai'
import { test, given } from 'sazerac'
import sinon from 'sinon'
import { parseDate, formatDate, formatRelativeDate, age } from './date.js'
import { InvalidResponse } from './index.js'
describe('parseDate', function () {
it('parses valid inputs', function () {
expect(parseDate('2024-01-01').valueOf()).to.equal(
new Date('2024-01-01').valueOf(),
)
expect(parseDate('Jan 01 01:00:00 2024 GMT').valueOf()).to.equal(
new Date('2024-01-01T01:00:00.000Z').valueOf(),
)
expect(parseDate('31/01/2024', 'DD/MM/YYYY').valueOf()).to.equal(
new Date('2024-01-31T00:00:00.000Z').valueOf(),
)
})
it('throws when given invalid inputs', function () {
// not a date
expect(() => parseDate('foo')).to.throw(InvalidResponse)
expect(() => parseDate([])).to.throw(InvalidResponse)
expect(() => parseDate(null)).to.throw(InvalidResponse)
// invalid dates (only works with format string)
expect(() => parseDate('2024-02-31', 'YYYY-MM-DD')).to.throw(
InvalidResponse,
)
expect(() => parseDate('2024-12-32', 'YYYY-MM-DD')).to.throw(
InvalidResponse,
)
// non-standard format with no format string
expect(() => parseDate('31/01/2024')).to.throw(InvalidResponse)
// parse format doesn't match date
expect(() => parseDate('2024-01-01', 'YYYYMMDDHHmmss')).to.throw(
InvalidResponse,
)
})
test(formatDate, () => {
given(1465513200000)
.describe('when given a timestamp in june 2016')
.expect('june 2016')
})
context('in october', function () {
let clock
beforeEach(function () {
clock = sinon.useFakeTimers(new Date(2017, 9, 15).getTime())
})
afterEach(function () {
clock.restore()
})
test(formatDate, () => {
given(new Date(2017, 0, 1).getTime())
.describe('when given the beginning of this year')
.expect('january')
})
})
context('in october', function () {
let clock
beforeEach(function () {
clock = sinon.useFakeTimers(new Date(2018, 9, 29).getTime())
})
afterEach(function () {
clock.restore()
})
test(formatRelativeDate, () => {
given(new Date(2018, 9, 31).getTime() / 1000)
.describe('when given the end of october')
.expect('in 2 days')
})
test(formatRelativeDate, () => {
given(new Date(2018, 9, 1).getTime() / 1000)
.describe('when given the beginning of october')
.expect('a month ago')
})
test(formatRelativeDate, () => {
given(9999999999999)
.describe('when given invalid date')
.expect('invalid date')
})
})
const monthsAgo = months => {
const result = new Date()
// This looks wack but it works.
result.setMonth(result.getMonth() - months)
return result
}
test(age, () => {
given(Date.now())
.describe('when given the current timestamp')
.expect('brightgreen')
given(new Date())
.describe('when given the current Date')
.expect('brightgreen')
given(new Date(2001, 1, 1))
.describe('when given a Date many years ago')
.expect('red')
given(monthsAgo(2))
.describe('when given a Date two months ago')
.expect('yellowgreen')
given(monthsAgo(15))
.describe('when given a Date 15 months ago')
.expect('orange')
// --- reversed --- //
given(Date.now(), true)
.describe('when given the current timestamp and reversed')
.expect('red')
given(new Date(), true)
.describe('when given the current Date and reversed')
.expect('red')
given(new Date(2001, 1, 1), true)
.describe('when given a Date many years ago and reversed')
.expect('brightgreen')
given(monthsAgo(2), true)
.describe('when given a Date two months ago and reversed')
.expect('yellow')
given(monthsAgo(15), true)
.describe('when given a Date 15 months ago and reversed')
.expect('green')
})
})

View File

@@ -1,4 +1,4 @@
import { formatRelativeDate } from '../text-formatters.js'
import { formatRelativeDate } from '../date.js'
import { BaseService, pathParams } from '../index.js'
const description = `

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { latest } from '../version.js'
import { BaseJsonService, NotFound, pathParams, queryParams } from '../index.js'
@@ -124,10 +124,6 @@ export default class DockerSize extends BaseJsonService {
static defaultBadgeData = { label: 'image size', color: 'blue' }
static render({ size }) {
return { message: prettyBytes(size) }
}
async fetch({ user, repo, tag, page }) {
page = page ? `&page=${page}` : ''
return await fetch(this, {
@@ -233,6 +229,6 @@ export default class DockerSize extends BaseJsonService {
}
const { size } = await this.transform({ tag, sort, data, arch })
return this.constructor.render({ size })
return renderSizeBadge(size, 'iec', 'image size')
}
}

View File

@@ -1,4 +1,4 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
@@ -6,35 +6,35 @@ t.create('docker image size (valid, library)')
.get('/_/alpine.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})
t.create('docker image size (valid, library, arch parameter )')
.get('/_/mysql.json?arch=amd64')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})
t.create('docker image size (valid, library with tag)')
.get('/_/alpine/latest.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})
t.create('docker image size (valid, user)')
.get('/jrottenberg/ffmpeg.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})
t.create('docker image size (valid, user with tag)')
.get('/jrottenberg/ffmpeg/3.2-alpine.json')
.expectBadge({
label: 'image size',
message: isFileSize,
message: isIecFileSize,
})
t.create('docker image size (invalid, incorrect tag)')

View File

@@ -3,10 +3,10 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('docker version (valid, library)')
.get('/_/memcached.json')
.get('/docker/example-voting-app-vote.json')
.expectBadge({
label: 'version',
message: isSemver,
message: 'latest',
})
t.create('docker version (valid, library with tag)')

View File

@@ -1,8 +1,7 @@
import chai from 'chai'
import { expect, use } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import jsonPath from './json-path.js'
const { expect } = chai
chai.use(chaiAsPromised)
use(chaiAsPromised)
describe('JSON Path service factory', function () {
describe('fetch()', function () {

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { pathParams } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { nonNegativeInteger } from '../validators.js'
import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
@@ -34,19 +33,12 @@ export default class EclipseMarketplaceUpdate extends EclipseMarketplaceBase {
static defaultBadgeData = { label: 'updated' }
static render({ date }) {
return {
message: formatDate(date),
color: ageColor(date),
}
}
async handle({ name }) {
const { marketplace } = await this.fetch({
name,
schema: updateResponseSchema,
})
const date = 1000 * parseInt(marketplace.node.changed)
return this.constructor.render({ date })
return renderDateBadge(date)
}
}

View File

@@ -35,6 +35,7 @@ const endpointSchema = Joi.object({
namedLogo: Joi.string(),
logoSvg: Joi.string(),
logoColor: optionalStringWhenNamedLogoPresent,
logoSize: optionalStringWhenNamedLogoPresent,
logoWidth: optionalNumberWhenAnyLogoPresent,
style: Joi.string(),
cacheSeconds: Joi.number().integer().min(0),

View File

@@ -93,6 +93,14 @@ The endpoint badge takes a single required query param: <code>url</code>, which
the query string. Only works for simple-icons logos.
</td>
</tr>
<tr>
<td><code>logoSize</code></td>
<td>
Default: none. Make icons adaptively resize by setting <code>auto</code>.
Useful for some wider logos like <code>amd</code> and <code>amg</code>.
Supported for simple-icons logos only.
</td>
</tr>
<tr>
<td><code>logoWidth</code></td>
<td>
@@ -147,6 +155,7 @@ export default class Endpoint extends BaseJsonService {
namedLogo,
logoSvg,
logoColor,
logoSize,
logoWidth,
style,
cacheSeconds,
@@ -160,6 +169,7 @@ export default class Endpoint extends BaseJsonService {
namedLogo,
logoSvg,
logoColor,
logoSize,
logoWidth,
style,
// don't allow the user to set cacheSeconds any shorter than this._cacheLength

View File

@@ -82,6 +82,22 @@ t.create('named logo with color')
expect(body).to.include(getSimpleIcon({ name: 'github', color: 'blue' }))
})
t.create('named logo with size')
.get('.svg?url=https://example.com/badge')
.intercept(nock =>
nock('https://example.com/').get('/badge').reply(200, {
schemaVersion: 1,
label: 'hey',
message: 'yo',
namedLogo: 'github',
logoSize: 'auto',
}),
)
.after((err, res, body) => {
expect(err).not.to.be.ok
expect(body).to.include(getSimpleIcon({ name: 'github', size: 'auto' }))
})
const logoSvg = Buffer.from(
getSimpleIcon({ name: 'npm' }).replace('data:image/svg+xml;base64,', ''),
'base64',

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { BaseJsonService, pathParams } from '../index.js'
import { age } from '../color-formatters.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { nonNegativeInteger } from '../validators.js'
import { renderDownloadsBadge } from '../downloads.js'
import { renderVersionBadge } from '../version.js'
@@ -131,18 +130,9 @@ class FactorioModPortalLastUpdated extends BaseFactorioModPortalService {
static defaultBadgeData = { label: 'last updated' }
static render({ lastUpdated }) {
return {
message: formatDate(lastUpdated),
color: age(lastUpdated),
}
}
async handle({ modName }) {
const resp = await this.fetch({ modName })
return this.constructor.render({
lastUpdated: resp.latest_release.released_at,
})
return renderDateBadge(resp.latest_release.released_at)
}
}

View File

@@ -1,5 +1,5 @@
import { pathParams } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import BaseGalaxyToolshedService from './galaxytoolshed-base.js'
export default class GalaxyToolshedCreatedDate extends BaseGalaxyToolshedService {
@@ -29,11 +29,6 @@ export default class GalaxyToolshedCreatedDate extends BaseGalaxyToolshedService
static defaultBadgeData = {
label: 'created date',
color: 'blue',
}
static render({ date }) {
return { message: formatDate(date) }
}
async handle({ repository, owner }) {
@@ -42,6 +37,6 @@ export default class GalaxyToolshedCreatedDate extends BaseGalaxyToolshedService
owner,
})
const { create_time: date } = response[0]
return this.constructor.render({ date })
return renderDateBadge(date, true)
}
}

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { optionalUrl, relativeUri } from '../validators.js'
import GiteaBase from './gitea-base.js'
import { description, httpErrorsFor } from './gitea-helper.js'
@@ -114,13 +113,6 @@ export default class GiteaLastCommit extends GiteaBase {
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ user, repo, branch, baseUrl, path }) {
// https://gitea.com/api/swagger#/repository
return super.fetch({
@@ -146,8 +138,6 @@ export default class GiteaLastCommit extends GiteaBase {
baseUrl,
path,
})
return this.constructor.render({
commitDate: body[0].commit[displayTimestamp].date,
})
return renderDateBadge(body[0].commit[displayTimestamp].date)
}
}

View File

@@ -114,10 +114,11 @@ describe('Github token acceptor', function () {
const res = await got.post(`${baseUrl}/github-auth/done`, {
body: form,
})
expect(res.body).to.startWith(
'<p>Shields.io has received your app-specific GitHub user token.',
)
expect(
res.body.startsWith(
'<p>Shields.io has received your app-specific GitHub user token.',
),
).to.be.true
expect(onTokenAccepted).to.have.been.calledWith(fakeAccessToken)
})
})

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { pathParams } from '../../index.js'
import { formatDate } from '../../text-formatters.js'
import { age as ageColor } from '../../color-formatters.js'
import { renderDateBadge } from '../../date.js'
import { GithubAuthV3Service } from '../github-auth-service.js'
import { documentation, httpErrorsFor } from '../github-helpers.js'
@@ -27,13 +26,6 @@ export default class GistLastCommit extends GithubAuthV3Service {
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ gistId }) {
return this._requestJson({
url: `/gists/${gistId}`,
@@ -44,6 +36,6 @@ export default class GistLastCommit extends GithubAuthV3Service {
async handle({ gistId }) {
const { updated_at: commitDate } = await this.fetch({ gistId })
return this.constructor.render({ commitDate })
return renderDateBadge(commitDate)
}
}

View File

@@ -1,5 +1,5 @@
import prettyBytes from 'pretty-bytes'
import { pathParams } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { BaseGithubLanguage } from './github-languages-base.js'
import { documentation } from './github-helpers.js'
@@ -31,15 +31,8 @@ export default class GithubCodeSize extends BaseGithubLanguage {
static defaultBadgeData = { label: 'code size' }
static render({ size }) {
return {
message: prettyBytes(size),
color: 'blue',
}
}
async handle({ user, repo }) {
const data = await this.fetch({ user, repo })
return this.constructor.render({ size: this.getTotalSize(data) })
return renderSizeBadge(this.getTotalSize(data), 'iec', 'code size')
}
}

View File

@@ -1,4 +1,4 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
@@ -6,7 +6,7 @@ t.create('code size in bytes for all languages')
.get('/badges/shields.json')
.expectBadge({
label: 'code size',
message: isFileSize,
message: isIecFileSize,
})
t.create('code size in bytes for all languages (empty repo)')

View File

@@ -1,8 +1,6 @@
import dayjs from 'dayjs'
import Joi from 'joi'
import { age } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { pathParams } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
@@ -34,14 +32,6 @@ export default class GithubCreatedAt extends GithubAuthV3Service {
static defaultBadgeData = { label: 'created at' }
static render({ createdAt }) {
const date = dayjs(createdAt)
return {
message: formatDate(date),
color: age(date, true),
}
}
async handle({ user, repo }) {
const { created_at: createdAt } = await this._requestJson({
schema,
@@ -49,6 +39,6 @@ export default class GithubCreatedAt extends GithubAuthV3Service {
httpErrors: httpErrorsFor('repo not found'),
})
return this.constructor.render({ createdAt })
return renderDateBadge(createdAt, true)
}
}

View File

@@ -2,6 +2,7 @@ import gql from 'graphql-tag'
import Joi from 'joi'
import dayjs from 'dayjs'
import { pathParam, queryParam } from '../index.js'
import { parseDate } from '../date.js'
import { metric, maybePluralize } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV4Service } from './github-auth-service.js'
@@ -97,7 +98,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
// The global cutoff time is 11/1 noon UTC.
// https://github.com/badges/shields/pull/4109#discussion_r330782093
// We want to show "1 day left" on the last day so we add 1.
daysLeft = dayjs(`${year}-11-01 12:00:00 Z`).diff(dayjs(), 'days') + 1
daysLeft = parseDate(`${year}-11-01 12:00:00 Z`).diff(dayjs(), 'days') + 1
}
if (daysLeft < 0) {
return {
@@ -181,7 +182,10 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
}
static getCalendarPosition(year) {
const daysToStart = dayjs(`${year}-10-01 00:00:00 Z`).diff(dayjs(), 'days')
const daysToStart = parseDate(`${year}-10-01 00:00:00 Z`).diff(
dayjs(),
'days',
)
const isBefore = daysToStart > 0
return { daysToStart, isBefore }
}

View File

@@ -2,10 +2,9 @@ import { colorScale } from '../color-formatters.js'
import { InvalidResponse, NotFound } from '../index.js'
const documentation = `
If your GitHub badge errors, it might be because you hit GitHub's rate limits.
You can increase Shields.io's rate limit by
[adding the Shields GitHub application](https://img.shields.io/github-auth)
using your GitHub account.
You can help increase Shields.io's rate limit by
[authorizing the Shields.io GitHub application](https://img.shields.io/github-auth).
Read more about [how it works](/blog/token-pool).
`
function issueStateColor(s) {

View File

@@ -1,7 +1,7 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { formatDate, metric } from '../text-formatters.js'
import { age } from '../color-formatters.js'
import { metric } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { InvalidResponse, pathParams } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import {
@@ -133,11 +133,13 @@ const ageUpdateMap = {
}).required(),
transform: ({ json, property }) =>
property === 'age' ? json.created_at : json.updated_at,
render: ({ property, value }) => ({
color: age(value),
label: property === 'age' ? 'created' : 'updated',
message: formatDate(value),
}),
render: ({ property, value }) => {
const label = property === 'age' ? 'created' : 'updated'
return {
...renderDateBadge(value),
label,
}
},
}
const milestoneMap = {

View File

@@ -1,7 +1,7 @@
import { expect } from 'chai'
import { test, given } from 'sazerac'
import { age } from '../color-formatters.js'
import { formatDate, metric } from '../text-formatters.js'
import { age, formatDate } from '../date.js'
import { metric } from '../text-formatters.js'
import { InvalidResponse } from '../index.js'
import GithubIssueDetail from './github-issue-detail.service.js'
import { issueStateColor, commentsColor } from './github-helpers.js'

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { relativeUri } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
@@ -88,13 +87,6 @@ export default class GithubLastCommit extends GithubAuthV3Service {
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ user, repo, branch, path }) {
return this._requestJson({
url: `/repos/${user}/${repo}/commits`,
@@ -111,8 +103,6 @@ export default class GithubLastCommit extends GithubAuthV3Service {
if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
return this.constructor.render({
commitDate: commit[displayTimestamp].date,
})
return renderDateBadge(commit[displayTimestamp].date)
}
}

View File

@@ -11,14 +11,14 @@ export const t = new ServiceTester({
t.create('Manifest version')
.get('/v/sindresorhus/show-all-github-issues.json')
.expectBadge({
label: 'version',
label: 'manifest',
message: isVPlusDottedVersionAtLeastOne,
})
t.create('Manifest version (path)')
.get('/v/RedSparr0w/IndieGala-Helper.json?filename=extension/manifest.json')
.expectBadge({
label: 'version',
label: 'manifest',
message: isVPlusDottedVersionAtLeastOne,
})

View File

@@ -1,13 +1,13 @@
import Joi from 'joi'
import { ServiceTester } from '../tester.js'
import {
isCommitHash,
isVPlusDottedVersionAtLeastOne,
isVPlusDottedVersionNClausesWithOptionalSuffix,
} from '../test-validators.js'
// e.g. v19.3b0
const isBlackVersion = Joi.string().regex(/^v\d+(\.\d+)*(.*)?$/)
const isShortSha = Joi.string().regex(/[0-9a-f]{7}/)
export const t = new ServiceTester({
id: 'GithubPipenv',
@@ -82,10 +82,8 @@ t.create('Locked version of unknown dependency')
})
t.create('Locked version of VCS dependency')
.get(
'/locked/dependency-version/thorn-oss/perception/dev/videoalignment.json',
)
.get('/locked/dependency-version/pypa/pipenv/dev/pypiserver.json')
.expectBadge({
label: 'videoalignment',
message: isShortSha,
label: 'pypiserver',
message: isCommitHash,
})

View File

@@ -1,8 +1,6 @@
import dayjs from 'dayjs'
import Joi from 'joi'
import { pathParam, queryParam } from '../index.js'
import { age } from '../color-formatters.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
@@ -63,14 +61,6 @@ export default class GithubReleaseDate extends GithubAuthV3Service {
static defaultBadgeData = { label: 'release date' }
static render({ date }) {
const releaseDate = dayjs(date)
return {
message: formatDate(releaseDate),
color: age(releaseDate),
}
}
async fetch({ variant, user, repo }) {
const url =
variant === 'release-date'
@@ -86,10 +76,8 @@ export default class GithubReleaseDate extends GithubAuthV3Service {
async handle({ variant, user, repo }, queryParams) {
const body = await this.fetch({ variant, user, repo })
if (Array.isArray(body)) {
return this.constructor.render({
date: body[0][queryParams.display_date],
})
return renderDateBadge(body[0][queryParams.display_date])
}
return this.constructor.render({ date: body[queryParams.display_date] })
return renderDateBadge(body[queryParams.display_date])
}
}

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { addv } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { redirector, pathParam, queryParam } from '../index.js'
import { renderVersionBadge } from '../version.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import {
fetchLatestRelease,
@@ -46,13 +45,6 @@ class GithubRelease extends GithubAuthV3Service {
static defaultBadgeData = { label: 'release' }
static render({ version, sort, isPrerelease }) {
let color = 'blue'
color = sort === 'semver' ? versionColor(version) : color
color = isPrerelease ? 'orange' : color
return { message: addv(version), color }
}
static transform(latestRelease, display) {
const { name, tag_name: tagName, prerelease: isPrerelease } = latestRelease
if (display === 'tag') {
@@ -72,9 +64,8 @@ class GithubRelease extends GithubAuthV3Service {
latestRelease,
queryParams.display_name,
)
return this.constructor.render({
return renderVersionBadge({
version,
sort: queryParams.sort,
isPrerelease,
})
}

View File

@@ -1,6 +1,6 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { pathParams } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { documentation, httpErrorsFor } from './github-helpers.js'
@@ -33,14 +33,6 @@ export default class GithubRepoSize extends GithubAuthV3Service {
static defaultBadgeData = { label: 'repo size' }
static render({ size }) {
return {
// note the GH API returns size in Kb
message: prettyBytes(size * 1024),
color: 'blue',
}
}
async fetch({ user, repo }) {
return this._requestJson({
url: `/repos/${user}/${repo}`,
@@ -51,6 +43,8 @@ export default class GithubRepoSize extends GithubAuthV3Service {
async handle({ user, repo }) {
const { size } = await this.fetch({ user, repo })
return this.constructor.render({ size })
// note the GH API returns size in KiB
// so we multiply by 1024 to get a size in bytes and then format that in IEC bytes
return renderSizeBadge(size * 1024, 'iec', 'repo size')
}
}

View File

@@ -1,10 +1,10 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('repository size').get('/badges/shields.json').expectBadge({
label: 'repo size',
message: isFileSize,
message: isIecFileSize,
})
t.create('repository size (repo not found)')

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import { pathParams } from '../index.js'
import { queryParams, redirector } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -7,33 +7,35 @@ import { documentation } from './github-helpers.js'
const schema = Joi.object({ total_count: nonNegativeInteger }).required()
export default class GithubSearch extends GithubAuthV3Service {
const queryParamSchema = Joi.object({
query: Joi.string().required(),
}).required()
const codeSearchDocs = `
For a full list of available filters and allowed values,
see GitHub's documentation on
[Searching code](https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax)`
class GitHubCodeSearch extends GithubAuthV3Service {
static category = 'analysis'
static route = {
base: 'github/search',
pattern: ':user/:repo/:query+',
base: 'github',
pattern: 'search',
queryParamSchema,
}
static openApi = {
'/github/search/{user}/{repo}/{query}': {
'/github/search': {
get: {
summary: 'GitHub search hit counter',
summary: 'GitHub code search count',
description: documentation,
parameters: pathParams(
{
name: 'user',
example: 'torvalds',
},
{
name: 'repo',
example: 'linux',
},
{
name: 'query',
example: 'goto',
},
),
parameters: queryParams({
name: 'query',
description: codeSearchDocs,
example: 'goto language:javascript NOT is:fork NOT is:archived',
required: true,
}),
},
},
}
@@ -50,21 +52,35 @@ export default class GithubSearch extends GithubAuthV3Service {
}
}
async handle({ user, repo, query }) {
async handle(_routeParams, { query }) {
const { total_count: totalCount } = await this._requestJson({
url: '/search/code',
options: {
searchParams: {
q: `${query} repo:${user}/${repo}`,
q: query,
},
},
schema,
httpErrors: {
401: 'auth required for search api',
404: 'repo not found',
422: 'repo not found',
},
})
return this.constructor.render({ query, totalCount })
}
}
const GitHubCodeSearchRedirect = redirector({
category: 'analysis',
route: {
base: 'github/search',
pattern: ':user/:repo/:query+',
},
transformPath: () => '/github/search',
transformQueryParams: ({ query, user, repo }) => ({
query: `${query} repo:${user}/${repo}`,
}),
dateAdded: new Date('2024-11-29'),
})
export { GitHubCodeSearch, GitHubCodeSearchRedirect }

View File

@@ -3,9 +3,18 @@ import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('hit counter')
.get('/badges/shields/async%20handle.json')
.get('/search.json?query=async%20handle')
.expectBadge({ label: 'async handle counter', message: isMetric })
t.create('hit counter for nonexistent repo')
.get('/badges/puppets/async%20handle.json')
.expectBadge({ label: 'async handle counter', message: '0' })
t.create('hit counter, zero results')
.get('/search.json?query=async%20handle%20repo%3Abadges%2Fpuppets')
.expectBadge({
label: 'async handle repo:badges/puppets counter',
message: '0',
})
t.create('legacy redirect')
.get('/search/badges/shields/async%20handle.svg')
.expectRedirect(
'/github/search.svg?query=async%20handle%20repo%3Abadges%2Fshields',
)

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { renderSizeBadge } from '../size.js'
import { nonNegativeInteger } from '../validators.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
@@ -44,13 +44,6 @@ export default class GithubSize extends GithubAuthV3Service {
},
}
static render({ size }) {
return {
message: prettyBytes(size),
color: 'blue',
}
}
async fetch({ user, repo, path, branch }) {
if (branch) {
return this._requestJson({
@@ -73,6 +66,6 @@ export default class GithubSize extends GithubAuthV3Service {
if (Array.isArray(body)) {
throw new NotFound({ prettyMessage: 'not a regular file' })
}
return this.constructor.render({ size: body.size })
return renderSizeBadge(body.size, 'iec')
}
}

View File

@@ -1,10 +1,10 @@
import { isFileSize } from '../test-validators.js'
import { isIecFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('File size')
.get('/webcaetano/craft/build/phaser-craft.min.js.json')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })
t.create('File size 404')
.get('/webcaetano/craft/build/does-not-exist.min.js.json')
@@ -20,12 +20,12 @@ t.create('File size for "not a regular file"')
t.create('File size for a specified branch')
.get('/webcaetano/craft/build/craft.min.js.json?branch=version-2')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })
t.create('File size for a specified tag')
.get('/webcaetano/craft/build/phaser-craft.min.js.json?branch=2.1.2')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })
t.create('File size for a specified commit')
.get('/webcaetano/craft/build/phaser-craft.min.js.json?branch=b848dbb')
.expectBadge({ label: 'size', message: isFileSize })
.expectBadge({ label: 'size', message: isIecFileSize })

View File

@@ -1,9 +1,7 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { matcher } from 'matcher'
import { addv } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { latest } from '../version.js'
import { latest, renderVersionBadge } from '../version.js'
import { NotFound, redirector, pathParam } from '../index.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import {
@@ -55,13 +53,6 @@ class GithubTag extends GithubAuthV4Service {
label: 'tag',
}
static render({ version, sort }) {
return {
message: addv(version),
color: sort === 'semver' ? versionColor(version) : 'blue',
}
}
static getLimit({ sort, filter }) {
if (!filter && sort === 'date') {
return 1
@@ -123,13 +114,12 @@ class GithubTag extends GithubAuthV4Service {
const prettyMessage = filter ? 'no matching tags found' : 'no tags found'
throw new NotFound({ prettyMessage })
}
return this.constructor.render({
return renderVersionBadge({
version: this.constructor.getLatestTag({
tags,
sort,
includePrereleases,
}),
sort,
})
}
}

View File

@@ -43,17 +43,6 @@ describe('GithubTag', function () {
}).expect('1.2.0-beta')
})
test(GithubTag.render, () => {
given({ usingSemver: false, version: '1.2.3' }).expect({
message: 'v1.2.3',
color: 'blue',
})
given({ usingSemver: true, version: '2.0.0' }).expect({
message: 'v2.0.0',
color: 'blue',
})
})
test(GithubTag.getLimit, () => {
given({ sort: 'date', filter: undefined }).expect(1)
given({ sort: 'date', filter: '' }).expect(1)

View File

@@ -1,13 +1,12 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
import { isDecimalPercentage } from '../test-validators.js'
export const t = await createServiceTester()
t.create('top language')
.get('/badges/shields.json')
.expectBadge({
label: 'javascript',
message: Joi.string().regex(/^([1-9]?[0-9]\.[0-9]|100\.0)%$/),
})
t.create('top language').get('/badges/shields.json').expectBadge({
label: 'javascript',
message: isDecimalPercentage,
})
t.create('top language (empty repo)')
.get('/pyvesb/emptyrepo.json')

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { age as ageColor } from '../color-formatters.js'
import { renderDateBadge } from '../date.js'
import { NotFound, pathParam, queryParam } from '../index.js'
import { formatDate } from '../text-formatters.js'
import { optionalUrl, relativeUri } from '../validators.js'
import GitLabBase from './gitlab-base.js'
import { description, httpErrorsFor } from './gitlab-helper.js'
@@ -66,13 +65,6 @@ export default class GitlabLastCommit extends GitLabBase {
static defaultBadgeData = { label: 'last commit' }
static render({ commitDate }) {
return {
message: formatDate(commitDate),
color: ageColor(Date.parse(commitDate)),
}
}
async fetch({ project, baseUrl, ref, path }) {
// https://docs.gitlab.com/ee/api/commits.html#list-repository-commits
return super.fetch({
@@ -94,6 +86,6 @@ export default class GitlabLastCommit extends GitLabBase {
if (!commit) throw new NotFound({ prettyMessage: 'no commits found' })
return this.constructor.render({ commitDate: commit.committed_date })
return renderDateBadge(commit.committed_date)
}
}

View File

@@ -0,0 +1,79 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { InvalidResponse, pathParam, queryParam } from '../index.js'
import GitLabBase from './gitlab-base.js'
import { description, httpErrorsFor } from './gitlab-helper.js'
const schema = Joi.object()
.pattern(
Joi.string().required(),
Joi.number().min(0).max(100).precision(2).required(),
)
.required()
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
}).required()
export default class GitlabTopLanguage extends GitLabBase {
static category = 'analysis'
static route = {
base: 'gitlab/languages',
pattern: ':project+',
queryParamSchema,
}
static openApi = {
'/gitlab/languages/{project}': {
get: {
summary: 'GitLab Top Language',
description,
parameters: [
pathParam({
name: 'project',
example: 'gitlab-org/gitlab',
}),
queryParam({
name: 'gitlab_url',
example: 'https://gitlab.com',
}),
],
},
},
}
static defaultBadgeData = { label: 'language' }
static render({ languageData }) {
const topLanguage = Object.keys(languageData).reduce((a, b) =>
languageData[a] > languageData[b] ? a : b,
)
return {
label: topLanguage.toLowerCase(),
message: `${languageData[topLanguage].toFixed(1)}%`,
color: 'blue',
}
}
async fetch({ project, baseUrl }) {
return super.fetch({
schema,
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}/languages`,
httpErrors: httpErrorsFor('project not found'),
})
}
async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
const languageData = await this.fetch({
project,
baseUrl,
})
if (Object.keys(languageData).length > 0) {
return this.constructor.render({ languageData })
} else {
throw new InvalidResponse({ prettyMessage: 'no languages found' })
}
}
}

View File

@@ -0,0 +1,23 @@
import { createServiceTester } from '../tester.js'
import { isDecimalPercentage } from '../test-validators.js'
export const t = await createServiceTester()
t.create('Valid Repository').get('/wireshark/wireshark.json').expectBadge({
label: 'c',
message: isDecimalPercentage,
})
t.create('Valid Blank Repo')
.get('/KoruptTinker/gitlab-blank-repo.json')
.expectBadge({
label: 'language',
message: 'no languages found',
})
t.create('Invalid Repository')
.get('/wireshark/invalidexample.json')
.expectBadge({
label: 'language',
message: 'project not found',
})

View File

@@ -10,9 +10,9 @@ t.create('known issue')
.expectBadge({ label: 'kafka-2896', message: 'Resolved' })
t.create('no status color')
.get('/foo-123.json?baseUrl=http://issues.apache.org/jira')
.get('/foo-123.json?baseUrl=https://issues.apache.org/jira')
.intercept(nock =>
nock('http://issues.apache.org/jira/rest/api/2/issue')
nock('https://issues.apache.org/jira/rest/api/2/issue')
.get(`/${encodeURIComponent('foo-123')}`)
.reply(200, {
fields: {

View File

@@ -4,20 +4,20 @@ import { sprintId, sprintQueryString } from './jira-test-helpers.js'
export const t = await createServiceTester()
t.create('unknown sprint')
.get('/abc.json?baseUrl=https://jira.spring.io')
.get('/abc.json?baseUrl=https://issues.apache.org/jira')
.expectBadge({ label: 'jira', message: 'sprint not found' })
t.create('known sprint')
.get('/94.json?baseUrl=https://jira.spring.io')
.get('/3.json?baseUrl=https://issues.apache.org/jira')
.expectBadge({
label: 'completion',
message: isIntegerPercentage,
})
t.create('100% completion')
.get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
.get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
nock('http://issues.apache.org/jira/rest/api/2')
nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {
@@ -47,9 +47,9 @@ t.create('100% completion')
})
t.create('0% completion')
.get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
.get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
nock('http://issues.apache.org/jira/rest/api/2')
nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {
@@ -72,9 +72,9 @@ t.create('0% completion')
})
t.create('no issues in sprint')
.get(`/${sprintId}.json?baseUrl=http://issues.apache.org/jira`)
.get(`/${sprintId}.json?baseUrl=https://issues.apache.org/jira`)
.intercept(nock =>
nock('http://issues.apache.org/jira/rest/api/2')
nock('https://issues.apache.org/jira/rest/api/2')
.get('/search')
.query(sprintQueryString)
.reply(200, {

View File

@@ -1,12 +1,8 @@
import Joi from 'joi'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import dayjs from 'dayjs'
import { InvalidResponse, pathParams } from '../index.js'
import { pathParams } from '../index.js'
import { parseDate, renderDateBadge } from '../date.js'
import { nonNegativeInteger } from '../validators.js'
import { formatDate } from '../text-formatters.js'
import { age as ageColor } from '../color-formatters.js'
import MavenCentralBase from './maven-central-base.js'
dayjs.extend(customParseFormat)
const updateResponseSchema = Joi.object({
metadata: Joi.object({
@@ -38,13 +34,6 @@ export default class MavenCentralLastUpdate extends MavenCentralBase {
static defaultBadgeData = { label: 'last updated' }
static render({ date }) {
return {
message: formatDate(date),
color: ageColor(date),
}
}
async handle({ groupId, artifactId }) {
const { metadata } = await this.fetch({
groupId,
@@ -52,15 +41,11 @@ export default class MavenCentralLastUpdate extends MavenCentralBase {
schema: updateResponseSchema,
})
const date = dayjs(
const date = parseDate(
String(metadata.versioning.lastUpdated),
'YYYYMMDDHHmmss',
)
if (!date.isValid) {
throw new InvalidResponse({ prettyMessage: 'invalid date' })
}
return this.constructor.render({ date })
return renderDateBadge(date)
}
}

View File

@@ -3,12 +3,6 @@ import {
isMetric,
isVPlusDottedVersionNClausesWithOptionalSuffix,
} from '../test-validators.js'
import {
queryIndex,
nuGetV3VersionJsonWithDash,
nuGetV3VersionJsonFirstCharZero,
nuGetV3VersionJsonFirstCharNotZero,
} from '../nuget-fixtures.js'
import { invalidJSON } from '../response-fixtures.js'
export const t = new ServiceTester({
@@ -75,66 +69,6 @@ t.create('version (tenant)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
t.create('version (yellow badge)')
.get('/myget/mongodb/v/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonWithDash),
)
.expectBadge({
label: 'mongodb',
message: 'v1.2-beta',
color: 'yellow',
})
t.create('version (orange badge)')
.get('/myget/mongodb/v/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharZero),
)
.expectBadge({
label: 'mongodb',
message: 'v0.35',
color: 'orange',
})
t.create('version (blue badge)')
.get('/myget/mongodb/v/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharNotZero),
)
.expectBadge({
label: 'mongodb',
message: 'v1.2.7',
color: 'blue',
})
t.create('version (not found)')
.get('/myget/foo/v/not-a-real-package.json')
.expectBadge({ label: 'myget', message: 'package not found' })
@@ -148,66 +82,6 @@ t.create('version (pre) (valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
t.create('version (pre) (yellow badge)')
.get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonWithDash),
)
.expectBadge({
label: 'mongodb',
message: 'v1.2-beta',
color: 'yellow',
})
t.create('version (pre) (orange badge)')
.get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharZero),
)
.expectBadge({
label: 'mongodb',
message: 'v0.35',
color: 'orange',
})
t.create('version (pre) (blue badge)')
.get('/myget/mongodb/vpre/MongoDB.Driver.Core.json')
.intercept(nock =>
nock('https://www.myget.org')
.get('/F/mongodb/api/v3/index.json')
.reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amongodb.driver.core&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharNotZero),
)
.expectBadge({
label: 'mongodb',
message: 'v1.2.7',
color: 'blue',
})
t.create('version (pre) (not found)')
.get('/myget/foo/vpre/not-a-real-package.json')
.expectBadge({ label: 'myget', message: 'package not found' })

View File

@@ -9,14 +9,6 @@ const description = `<p>This badge indicates whether the package supports the <b
export default class NodeCurrentVersion extends NodeVersionBase {
static route = this.buildRoute('node/v', { withTag: true })
static defaultBadgeData = {
label: 'node',
}
static type = 'current'
static colorResolver = versionColorForRangeCurrent
static openApi = {
'/node/v/{packageName}': {
get: {
@@ -57,4 +49,12 @@ export default class NodeCurrentVersion extends NodeVersionBase {
},
},
}
static defaultBadgeData = {
label: 'node',
}
static type = 'current'
static colorResolver = versionColorForRangeCurrent
}

View File

@@ -9,14 +9,6 @@ const description = `<p>This badge indicates whether the package supports <b>all
export default class NodeLtsVersion extends NodeVersionBase {
static route = this.buildRoute('node/v-lts', { withTag: true })
static defaultBadgeData = {
label: 'node-lts',
}
static type = 'lts'
static colorResolver = versionColorForRangeLts
static openApi = {
'/node/v-lts/{packageName}': {
get: {
@@ -57,4 +49,12 @@ export default class NodeLtsVersion extends NodeVersionBase {
},
},
}
static defaultBadgeData = {
label: 'node-lts',
}
static type = 'lts'
static colorResolver = versionColorForRangeLts
}

View File

@@ -81,8 +81,11 @@ export default class NpmBase extends BaseJsonService {
}
async _requestJson(data) {
return super._requestJson(
this.authHelper.withBearerAuthHeader({
let payload
if (data?.options?.headers?.Accept) {
payload = data
} else {
payload = {
...data,
options: {
headers: {
@@ -91,8 +94,9 @@ export default class NpmBase extends BaseJsonService {
Accept: '*/*',
},
},
}),
)
}
}
return super._requestJson(this.authHelper.withBearerAuthHeader(payload))
}
async fetchPackageData({ registryUrl, scope, packageName, tag }) {
@@ -143,4 +147,37 @@ export default class NpmBase extends BaseJsonService {
return this.constructor._validate(packageData, packageDataSchema)
}
async fetch({
registryUrl,
scope,
packageName,
schema,
abbreviated = false,
}) {
registryUrl = registryUrl || this.constructor.defaultRegistryUrl
let url
if (scope === undefined) {
url = `${registryUrl}/${packageName}`
} else {
const scoped = this.constructor.encodeScopedPackage({
scope,
packageName,
})
url = `${registryUrl}/${scoped}`
}
// https://github.com/npm/registry/blob/main/docs/responses/package-metadata.md
const options = abbreviated
? { headers: { Accept: 'application/vnd.npm.install-v1+json' } }
: {}
return this._requestJson({
url,
schema,
options,
httpErrors: { 404: 'package not found' },
})
}
}

View File

@@ -0,0 +1,119 @@
import Joi from 'joi'
import { NotFound, pathParam, queryParam } from '../index.js'
import { renderDateBadge } from '../date.js'
import NpmBase, {
packageNameDescription,
queryParamSchema,
} from './npm-base.js'
const fullSchema = Joi.object({
time: Joi.object()
.pattern(Joi.string().required(), Joi.string().required())
.required(),
'dist-tags': Joi.object()
.pattern(Joi.string().required(), Joi.string().required())
.required(),
}).required()
const abbreviatedSchema = Joi.object({
modified: Joi.string().required(),
}).required()
export class NpmLastUpdateWithTag extends NpmBase {
static category = 'activity'
static route = {
base: 'npm/last-update',
pattern: ':scope(@[^/]+)?/:packageName/:tag',
queryParamSchema,
}
static openApi = {
'/npm/last-update/{packageName}/{tag}': {
get: {
summary: 'NPM Last Update (with dist tag)',
parameters: [
pathParam({
name: 'packageName',
example: 'verdaccio',
packageNameDescription,
}),
pathParam({
name: 'tag',
example: 'next-8',
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
}
static defaultBadgeData = { label: 'last updated' }
async handle(namedParams, queryParams) {
const { scope, packageName, tag, registryUrl } =
this.constructor.unpackParams(namedParams, queryParams)
const packageData = await this.fetch({
registryUrl,
scope,
packageName,
schema: fullSchema,
})
const tagVersion = packageData['dist-tags'][tag]
if (!tagVersion) {
throw new NotFound({ prettyMessage: 'tag not found' })
}
return renderDateBadge(packageData.time[tagVersion])
}
}
export class NpmLastUpdate extends NpmBase {
static category = 'activity'
static route = this.buildRoute('npm/last-update', { withTag: false })
static openApi = {
'/npm/last-update/{packageName}': {
get: {
summary: 'NPM Last Update',
parameters: [
pathParam({
name: 'packageName',
example: 'verdaccio',
packageNameDescription,
}),
queryParam({
name: 'registry_uri',
example: 'https://registry.npmjs.com',
}),
],
},
},
}
static defaultBadgeData = { label: 'last updated' }
async handle(namedParams, queryParams) {
const { scope, packageName, registryUrl } = this.constructor.unpackParams(
namedParams,
queryParams,
)
const packageData = await this.fetch({
registryUrl,
scope,
packageName,
schema: abbreviatedSchema,
abbreviated: true,
})
return renderDateBadge(packageData.modified)
}
}

View File

@@ -0,0 +1,81 @@
import { isFormattedDate } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('last updated date, no tag, valid package')
.get('/verdaccio.json')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, no tag, invalid package')
.get('/not-a-package.json')
.expectBadge({
label: 'last updated',
message: 'package not found',
})
t.create('last updated date, no tag, custom repository, valid package')
.get('/verdaccio.json?registry_uri=https://registry.npmjs.com')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, no tag, valid package with scope')
.get('/@npm/types.json')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, no tag, invalid package with scope')
.get('/@not-a-scoped-package/not-a-valid-package.json')
.expectBadge({
label: 'last updated',
message: 'package not found',
})
t.create('last updated date, with tag, valid package')
.get('/verdaccio/latest.json')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, with tag, invalid package')
.get('/not-a-package/doesnt-matter.json')
.expectBadge({
label: 'last updated',
message: 'package not found',
})
t.create('last updated date, with tag, invalid tag')
.get('/verdaccio/not-a-valid-tag.json')
.expectBadge({
label: 'last updated',
message: 'tag not found',
})
t.create('last updated date, with tag, custom repository, valid package')
.get('/verdaccio/latest.json?registry_uri=https://registry.npmjs.com')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, with tag, valid package with scope')
.get('/@npm/types/latest.json')
.expectBadge({
label: 'last updated',
message: isFormattedDate,
})
t.create('last updated date, with tag, invalid package with scope')
.get('/@not-a-scoped-package/not-a-valid-package/doesnt-matter.json')
.expectBadge({
label: 'last updated',
message: 'package not found',
})

View File

@@ -1,8 +1,11 @@
import Joi from 'joi'
import prettyBytes from 'pretty-bytes'
import { pathParam, queryParam } from '../index.js'
import { renderSizeBadge } from '../size.js'
import { optionalNonNegativeInteger } from '../validators.js'
import NpmBase, { packageNameDescription } from './npm-base.js'
import NpmBase, {
packageNameDescription,
queryParamSchema,
} from './npm-base.js'
const schema = Joi.object({
dist: Joi.object({
@@ -16,6 +19,7 @@ export default class NpmUnpackedSize extends NpmBase {
static route = {
base: 'npm/unpacked-size',
pattern: ':scope(@[^/]+)?/:packageName/:version*',
queryParamSchema,
}
static openApi = {
@@ -78,10 +82,13 @@ export default class NpmUnpackedSize extends NpmBase {
})
const { unpackedSize } = dist
if (unpackedSize) {
return renderSizeBadge(unpackedSize, 'metric', 'unpacked size')
}
return {
label: 'unpacked size',
message: unpackedSize ? prettyBytes(unpackedSize) : 'unknown',
color: unpackedSize ? 'blue' : 'lightgray',
message: 'unknown',
color: 'lightgray',
}
}
}

View File

@@ -1,11 +1,11 @@
import { isFileSize } from '../test-validators.js'
import { isMetricFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Latest unpacked size')
.get('/firereact.json')
.expectBadge({ label: 'unpacked size', message: isFileSize })
.expectBadge({ label: 'unpacked size', message: isMetricFileSize })
t.create('Nonexistent unpacked size with version')
.get('/express/4.16.0.json')
@@ -13,15 +13,15 @@ t.create('Nonexistent unpacked size with version')
t.create('Unpacked size with version')
.get('/firereact/0.7.0.json')
.expectBadge({ label: 'unpacked size', message: '147 kB' })
.expectBadge({ label: 'unpacked size', message: '147.2 kB' })
t.create('Unpacked size for scoped package')
.get('/@testing-library/react.json')
.expectBadge({ label: 'unpacked size', message: isFileSize })
.expectBadge({ label: 'unpacked size', message: isMetricFileSize })
t.create('Unpacked size for scoped package with version')
.get('/@testing-library/react/14.2.1.json')
.expectBadge({ label: 'unpacked size', message: '5.41 MB' })
.expectBadge({ label: 'unpacked size', message: '5.4 MB' })
t.create('Nonexistent unpacked size for scoped package with version')
.get('/@cycle/rx-run/7.2.0.json')

View File

@@ -1,57 +0,0 @@
const queryIndex = JSON.stringify({
resources: [
{
'@id': 'https://api-v2v3search-0.nuget.org/query',
'@type': 'SearchQueryService',
},
],
})
const nuGetV3VersionJsonWithDash = JSON.stringify({
data: [
{
totalDownloads: 0,
versions: [{ version: '1.2-beta' }],
},
],
})
const nuGetV3VersionJsonFirstCharZero = JSON.stringify({
data: [
{
totalDownloads: 0,
versions: [{ version: '0.35' }],
},
],
})
const nuGetV3VersionJsonFirstCharNotZero = JSON.stringify({
data: [
{
totalDownloads: 0,
versions: [{ version: '1.2.7' }],
},
],
})
const nuGetV3VersionJsonBuildMetadataWithDash = JSON.stringify({
data: [
{
totalDownloads: 0,
versions: [
{
version: '1.16.0+388',
},
{
version: '1.17.0+1b81349-429',
},
],
},
],
})
export {
queryIndex,
nuGetV3VersionJsonWithDash,
nuGetV3VersionJsonFirstCharZero,
nuGetV3VersionJsonFirstCharNotZero,
nuGetV3VersionJsonBuildMetadataWithDash,
}

View File

@@ -4,16 +4,35 @@ import {
isVPlusDottedVersionNClauses,
isVPlusDottedVersionNClausesWithOptionalSuffix,
} from '../test-validators.js'
import {
queryIndex,
nuGetV3VersionJsonFirstCharZero,
nuGetV3VersionJsonFirstCharNotZero,
nuGetV3VersionJsonBuildMetadataWithDash,
} from '../nuget-fixtures.js'
import { invalidJSON } from '../response-fixtures.js'
export const t = new ServiceTester({ id: 'nuget', title: 'NuGet' })
const queryIndex = JSON.stringify({
resources: [
{
'@id': 'https://api-v2v3search-0.nuget.org/query',
'@type': 'SearchQueryService',
},
],
})
const nuGetV3VersionJsonBuildMetadataWithDash = JSON.stringify({
data: [
{
totalDownloads: 0,
versions: [
{
version: '1.16.0+388',
},
{
version: '1.17.0+1b81349-429',
},
],
},
],
})
// downloads
t.create('total downloads (valid)')
@@ -50,59 +69,6 @@ t.create('version (valid)')
message: isVPlusDottedVersionNClauses,
})
t.create('version (orange badge)')
.get('/v/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharZero),
)
.expectBadge({
label: 'nuget',
message: 'v0.35',
color: 'orange',
})
t.create('version (blue badge)')
.get('/v/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharNotZero),
)
.expectBadge({
label: 'nuget',
message: 'v1.2.7',
color: 'blue',
})
// https://github.com/badges/shields/issues/4219
t.create('version (build metadata with -)')
.get('/v/MongoFramework.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get('/query?q=packageid%3Amongoframework&prerelease=true&semVerLevel=2')
.reply(200, nuGetV3VersionJsonBuildMetadataWithDash),
)
.expectBadge({
label: 'nuget',
message: 'v1.17.0',
color: 'blue',
})
t.create('version (not found)')
.get('/v/not-a-real-package.json')
.expectBadge({ label: 'nuget', message: 'package not found' })
@@ -121,6 +87,23 @@ t.create('version (unexpected second response)')
)
.expectBadge({ label: 'nuget', message: 'unparseable json response' })
// https://github.com/badges/shields/issues/4219
t.create('version (build metadata with -)')
.get('/v/MongoFramework.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get('/query?q=packageid%3Amongoframework&prerelease=true&semVerLevel=2')
.reply(200, nuGetV3VersionJsonBuildMetadataWithDash),
)
.expectBadge({
label: 'nuget',
message: 'v1.17.0',
color: 'blue',
})
// version (pre)
t.create('version (pre) (valid)')
@@ -130,56 +113,6 @@ t.create('version (pre) (valid)')
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})
t.create('version (pre) (orange badge)')
.get('/vpre/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharZero),
)
.expectBadge({
label: 'nuget',
message: 'v0.35',
color: 'orange',
})
t.create('version (pre) (blue badge)')
.get('/vpre/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
.reply(200, nuGetV3VersionJsonFirstCharNotZero),
)
.expectBadge({
label: 'nuget',
message: 'v1.2.7',
color: 'blue',
})
t.create('version (pre) (not found)')
.get('/vpre/not-a-real-package.json')
.expectBadge({ label: 'nuget', message: 'package not found' })
t.create('version (pre) (unexpected second response)')
.get('/vpre/Microsoft.AspNetCore.Mvc.json')
.intercept(nock =>
nock('https://api.nuget.org').get('/v3/index.json').reply(200, queryIndex),
)
.intercept(nock =>
nock('https://api-v2v3search-0.nuget.org')
.get(
'/query?q=packageid%3Amicrosoft.aspnetcore.mvc&prerelease=true&semVerLevel=2',
)
.reply(invalidJSON),
)
.expectBadge({ label: 'nuget', message: 'unparseable json response' })

View File

@@ -1,6 +1,5 @@
import { pathParams } from '../index.js'
import { age } from '../color-formatters.js'
import { formatDate } from '../text-formatters.js'
import { renderDateBadge } from '../date.js'
import { OpenVSXBase, description } from './open-vsx-base.js'
export default class OpenVSXReleaseDate extends OpenVSXBase {
@@ -32,17 +31,8 @@ export default class OpenVSXReleaseDate extends OpenVSXBase {
static defaultBadgeData = { label: 'release date' }
static render({ releaseDate }) {
return {
message: formatDate(releaseDate),
color: age(releaseDate),
}
}
async handle({ namespace, extension }) {
const { timestamp } = await this.fetch({ namespace, extension })
return this.constructor.render({
releaseDate: timestamp,
})
return renderDateBadge(timestamp)
}
}

View File

@@ -1,8 +1,7 @@
import chai from 'chai'
import { expect, use } from 'chai'
import chaiAsPromised from 'chai-as-promised'
import PackagistDependencyVersion from './packagist-dependency-version.service.js'
const { expect } = chai
chai.use(chaiAsPromised)
use(chaiAsPromised)
describe('PackagistDependencyVersion', function () {
const fullPackagistJson = {

View File

@@ -4,7 +4,7 @@ export const description = `
[PingPong](https://pingpong.one/) is a status page and monitoring service.
To see more details about this badge and obtain your api key, visit
[https://my.pingpong.one/integrations/badge-status/](https://my.pingpong.one/integrations/badge-status/)
[https://my.pingpong.one/integrations/badge-uptime/](https://my.pingpong.one/integrations/badge-uptime/)
`
export const baseUrl = 'https://api.pingpong.one/widget/shields'

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