diff --git a/.github/ISSUE_TEMPLATE/3_Badge_request.yml b/.github/ISSUE_TEMPLATE/3_Badge_request.yml index af72465f56157..c8424fe52d16b 100644 --- a/.github/ISSUE_TEMPLATE/3_Badge_request.yml +++ b/.github/ISSUE_TEMPLATE/3_Badge_request.yml @@ -12,7 +12,7 @@ body: **fetch and display data from an upstream service**. If your suggestion is for a static badge (which shows the same information every time it is requested), it is - [already possible to make these](https://github.com/badges/shields/blob/master/doc/static-badges.md). + [already possible to make these](https://shields.io/docs/static-badges). We don't add specific routes for badges which only show static information. - type: textarea @@ -25,7 +25,7 @@ body: - Which service is this badge for e.g: GitHub, Travis CI - What sort of information should this badge show? Provide an example in plain text e.g: "version | v1.01" or as a static badge - (static badge generator can be found at https://shields.io/#your-badge ) + (static badge generator can be found at https://shields.io/badges/static-badge ) validations: required: true diff --git a/.github/actions/draft-release/Dockerfile b/.github/actions/draft-release/Dockerfile index ae6571aae587e..f2935f02842e5 100644 --- a/.github/actions/draft-release/Dockerfile +++ b/.github/actions/draft-release/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12-buster +FROM node:18-bullseye RUN apt-get update RUN apt-get install -y jq diff --git a/.github/actions/draft-release/entrypoint.sh b/.github/actions/draft-release/entrypoint.sh index 46de1f43842fd..b4518b3d5ad82 100755 --- a/.github/actions/draft-release/entrypoint.sh +++ b/.github/actions/draft-release/entrypoint.sh @@ -2,14 +2,17 @@ set -euxo pipefail -# Set up a git user -git config user.name "release[bot]" -git config user.email "actions@users.noreply.github.com" +# mark workspace dir as 'safe' +git config --system --add safe.directory '/github/workspace' # Find last server-YYYY-MM-DD tag git fetch --unshallow --tags LAST_TAG=$(git tag | grep server | tail -n 1) +# Set up a git user +git config user.name "release[bot]" +git config user.email "actions@users.noreply.github.com" + # Find the marker in CHANGELOG.md INSERT_POINT=$(grep -n "^\-\-\-$" CHANGELOG.md | cut -f1 -d:) INSERT_POINT=$((INSERT_POINT+1)) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3f42a9adba6f..b36c94ba36959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m --- +## server-2023-08-01 + +- Convert `examples` arrays to `openApi` objects (part 1) [#9320](https://github.com/badges/shields/issues/9320) +- Migrate from docs.rs' builds API to status API [#9422](https://github.com/badges/shields/issues/9422) +- [OpenVSX] Fix OpenVSX API call for unversioned package URLs [#9408](https://github.com/badges/shields/issues/9408) +- Add support for [Lemmy] [#9368](https://github.com/badges/shields/issues/9368) +- upgrade to npm 9 [#9323](https://github.com/badges/shields/issues/9323) +- Go back to default YouTube cache [#9372](https://github.com/badges/shields/issues/9372) +- Add [GitHubDiscussionsSearch] and GitHubRepoDiscussionsSearch service [#9340](https://github.com/badges/shields/issues/9340) +- Allow user to filter github tags and releases [#9193](https://github.com/badges/shields/issues/9193) +- don't URL encode slash in [githubactionsworkflow] badge [#9322](https://github.com/badges/shields/issues/9322) +- add a bit of border to select boxes [#9348](https://github.com/badges/shields/issues/9348) +- deprecate [snyk] badges [#9349](https://github.com/badges/shields/issues/9349) +- increase max-age on [docker] badges, again [#9350](https://github.com/badges/shields/issues/9350) [#9369](https://github.com/badges/shields/issues/9369) +- Dependency updates + ## server-2023-07-02 By far the most significant change in this release is the long-awaited launch of the re-designed frontend: diff --git a/README.md b/README.md index 1c3bda987fc1b..41a158b7433bf 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ This repo hosts: [Make your own badges!][custom badges] (Quick example: `https://img.shields.io/badge/left-right-f39f37`) -[custom badges]: https://shields.io/#your-badge +[custom badges]: http://localhost:3000/badges/static-badge ### Quickstart diff --git a/core/base-service/base.js b/core/base-service/base.js index 270c3e20867b7..96efddbf920e2 100644 --- a/core/base-service/base.js +++ b/core/base-service/base.js @@ -189,6 +189,22 @@ class BaseService { this.examples.forEach((example, index) => validateExample(example, index, this), ) + + // ensure openApi spec matches route + if (this.openApi) { + const preparedRoute = prepareRoute(this.route) + for (const [key, value] of Object.entries(this.openApi)) { + let example = key + for (const param of value.get.parameters) { + example = example.replace(`{${param.name}}`, param.example) + } + if (!example.match(preparedRoute.regex)) { + throw new Error( + `Inconsistent Open Api spec and Route found for service ${this.name}`, + ) + } + } + } } static getDefinition() { diff --git a/core/base-service/index.js b/core/base-service/index.js index f249090501724..5808b6211d56a 100644 --- a/core/base-service/index.js +++ b/core/base-service/index.js @@ -15,6 +15,7 @@ import { Deprecated, ImproperlyConfigured, } from './errors.js' +import { pathParam, pathParams, queryParam, queryParams } from './openapi.js' export { BaseService, @@ -32,4 +33,8 @@ export { InvalidParameter, ImproperlyConfigured, Deprecated, + pathParam, + pathParams, + queryParam, + queryParams, } diff --git a/core/base-service/loader.js b/core/base-service/loader.js index 7cd272e0d0500..9b72355436299 100644 --- a/core/base-service/loader.js +++ b/core/base-service/loader.js @@ -83,6 +83,18 @@ async function loadServiceClasses(servicePaths) { }, ) + const routeSummaries = [] + serviceClasses.forEach(function (serviceClass) { + if (serviceClass.openApi) { + for (const route of Object.values(serviceClass.openApi)) { + routeSummaries.push(route.get.summary) + } + } + }) + assertNamesUnique(routeSummaries, { + message: 'Duplicate route summary found', + }) + return serviceClasses } diff --git a/core/base-service/openapi.js b/core/base-service/openapi.js index 97760dd1f059b..92847b71f52ae 100644 --- a/core/base-service/openapi.js +++ b/core/base-service/openapi.js @@ -1,3 +1,9 @@ +/** + * Functions for publishing the shields.io URL schema as an OpenAPI Document + * + * @module + */ + const baseUrl = process.env.BASE_URL const globalParamRefs = [ { $ref: '#/components/parameters/style' }, @@ -247,7 +253,7 @@ function category2openapi(category, services) { in: 'query', required: false, description: - 'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository.', + 'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository. Further info.', schema: { type: 'string', }, @@ -332,4 +338,132 @@ function category2openapi(category, services) { return spec } -export { category2openapi } +/** + * Helper function for assembling an OpenAPI path parameter object + * + * @param {module:core/base-service/openapi~PathParamInput} param Input param + * @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object + * @see https://swagger.io/specification/#parameter-object + */ +function pathParam({ + name, + example, + schema = { type: 'string' }, + description, +}) { + return { name, in: 'path', required: true, schema, example, description } +} + +/** + * Helper function for assembling an array of OpenAPI path parameter objects + * The code + * ``` + * const params = pathParams( + * { name: 'name1', example: 'example1' }, + * { name: 'name2', example: 'example2' }, + * ) + * ``` + * is equivilent to + * ``` + * const params = [ + * pathParam({ name: 'name1', example: 'example1' }), + * pathParam({ name: 'name2', example: 'example2' }), + * ] + * ``` + * + * @param {...module:core/base-service/openapi~PathParamInput} params Input params + * @returns {Array.} Array of OpenAPI Parameter Objects + * @see {@link module:core/base-service/openapi~pathParam} + */ +function pathParams(...params) { + return params.map(param => pathParam(param)) +} + +/** + * Helper function for assembling an OpenAPI query parameter object + * + * @param {module:core/base-service/openapi~QueryParamInput} param Input param + * @returns {module:core/base-service/openapi~OpenApiParam} OpenAPI Parameter Object + * @see https://swagger.io/specification/#parameter-object + */ +function queryParam({ + name, + example, + schema = { type: 'string' }, + required = false, + description, +}) { + const param = { name, in: 'query', required, schema, example, description } + if (example === null && schema.type === 'boolean') { + param.allowEmptyValue = true + } + return param +} + +/** + * Helper function for assembling an array of OpenAPI query parameter objects + * The code + * ``` + * const params = queryParams( + * { name: 'name1', example: 'example1' }, + * { name: 'name2', example: 'example2' }, + * ) + * ``` + * is equivilent to + * ``` + * const params = [ + * queryParam({ name: 'name1', example: 'example1' }), + * queryParams({ name: 'name2', example: 'example2' }), + * ] + * ``` + * + * @param {...module:core/base-service/openapi~QueryParamInput} params Input params + * @returns {Array.} Array of OpenAPI Parameter Objects + * @see {@link module:core/base-service/openapi~queryParam} + */ +function queryParams(...params) { + return params.map(param => queryParam(param)) +} + +/** + * @typedef {object} PathParamInput + * @property {string} name The name of the parameter. Parameter names are case sensitive + * @property {string} example Example of a valid value for this parameter + * @property {object} [schema={ type: 'string' }] Parameter schema. + * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object) + * specifying the parameter type. + * Normally this should be omitted as all path parameters are strings. + * Use this when we also want to pass an enum of valid parameters + * to be presented as a drop-down in the frontend. e.g: + * `{'type': 'string', 'enum': ['github', 'bitbucket'}` (Optional) + * @property {string} description A brief description of the parameter (Optional) + */ + +/** + * @typedef {object} QueryParamInput + * @property {string} name The name of the parameter. Parameter names are case sensitive + * @property {string|null} example Example of a valid value for this parameter + * @property {object} [schema={ type: 'string' }] Parameter schema. + * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object) + * specifying the parameter type. This can normally be omitted. + * Query params are usually strings. (Optional) + * @property {boolean} [required=false] Determines whether this parameter is mandatory (Optional) + * @property {string} description A brief description of the parameter (Optional) + */ + +/** + * OpenAPI Parameter Object + * + * @typedef {object} OpenApiParam + * @property {string} name The name of the parameter + * @property {string|null} example Example of a valid value for this parameter + * @property {('path'|'query')} in The location of the parameter + * @property {object} schema Parameter schema. + * An [OpenAPI Schema object](https://swagger.io/specification/#schema-object) + * specifying the parameter type. + * @property {boolean} required Determines whether this parameter is mandatory + * @property {string} description A brief description of the parameter + * @property {boolean} allowEmptyValue If true, allows the ability to pass an empty value to this parameter + */ + +export { category2openapi, pathParam, pathParams, queryParam, queryParams } diff --git a/core/base-service/openapi.spec.js b/core/base-service/openapi.spec.js index f36e514fcec6a..0577f078e682c 100644 --- a/core/base-service/openapi.spec.js +++ b/core/base-service/openapi.spec.js @@ -1,5 +1,11 @@ import chai from 'chai' -import { category2openapi } from './openapi.js' +import { + category2openapi, + pathParam, + pathParams, + queryParam, + queryParams, +} from './openapi.js' import BaseJsonService from './base-json.js' const { expect } = chai @@ -92,7 +98,7 @@ const expected = { in: 'query', required: false, description: - 'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository.', + 'One of the named logos (bitcoin, dependabot, gitlab, npm, paypal, serverfault, stackexchange, superuser, telegram, travis) or simple-icons. All simple-icons are referenced using icon slugs. You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository. Further info.', schema: { type: 'string' }, example: 'appveyor', }, @@ -376,3 +382,148 @@ describe('category2openapi', function () { ).to.deep.equal(expected) }) }) + +describe('pathParam, pathParams', function () { + it('generates a pathParam with defaults', function () { + const input = { name: 'name', example: 'example' } + const expected = { + name: 'name', + in: 'path', + required: true, + schema: { + type: 'string', + }, + example: 'example', + description: undefined, + } + expect(pathParam(input)).to.deep.equal(expected) + expect(pathParams(input)[0]).to.deep.equal(expected) + }) + + it('generates a pathParam with custom args', function () { + const input = { + name: 'name', + example: true, + schema: { type: 'boolean' }, + description: 'long desc', + } + const expected = { + name: 'name', + in: 'path', + required: true, + schema: { + type: 'boolean', + }, + example: true, + description: 'long desc', + } + expect(pathParam(input)).to.deep.equal(expected) + expect(pathParams(input)[0]).to.deep.equal(expected) + }) + + it('generates multiple pathParams', function () { + expect( + pathParams( + { name: 'name1', example: 'example1' }, + { name: 'name2', example: 'example2' }, + ), + ).to.deep.equal([ + { + name: 'name1', + in: 'path', + required: true, + schema: { + type: 'string', + }, + example: 'example1', + description: undefined, + }, + { + name: 'name2', + in: 'path', + required: true, + schema: { + type: 'string', + }, + example: 'example2', + description: undefined, + }, + ]) + }) +}) + +describe('queryParam, queryParams', function () { + it('generates a queryParam with defaults', function () { + const input = { name: 'name', example: 'example' } + const expected = { + name: 'name', + in: 'query', + required: false, + schema: { type: 'string' }, + example: 'example', + description: undefined, + } + expect(queryParam(input)).to.deep.equal(expected) + expect(queryParams(input)[0]).to.deep.equal(expected) + }) + + it('generates queryParam with custom args', function () { + const input = { + name: 'name', + example: 'example', + required: true, + description: 'long desc', + } + const expected = { + name: 'name', + in: 'query', + required: true, + schema: { type: 'string' }, + example: 'example', + description: 'long desc', + } + expect(queryParam(input)).to.deep.equal(expected) + expect(queryParams(input)[0]).to.deep.equal(expected) + }) + + it('generates a queryParam with boolean/null example', function () { + const input = { name: 'name', example: null, schema: { type: 'boolean' } } + const expected = { + name: 'name', + in: 'query', + required: false, + schema: { type: 'boolean' }, + allowEmptyValue: true, + example: null, + description: undefined, + } + expect(queryParam(input)).to.deep.equal(expected) + expect(queryParams(input)[0]).to.deep.equal(expected) + }) + + it('generates multiple queryParams', function () { + expect( + queryParams( + { name: 'name1', example: 'example1' }, + { name: 'name2', example: 'example2' }, + ), + ).to.deep.equal([ + { + name: 'name1', + in: 'query', + required: false, + schema: { type: 'string' }, + example: 'example1', + description: undefined, + }, + { + name: 'name2', + in: 'query', + required: false, + schema: { type: 'string' }, + example: 'example2', + description: undefined, + }, + ]) + }) +}) diff --git a/core/base-service/service-definitions.js b/core/base-service/service-definitions.js index 6687e5bd61fa4..c4beb656361c6 100644 --- a/core/base-service/service-definitions.js +++ b/core/base-service/service-definitions.js @@ -48,7 +48,7 @@ const serviceDefinition = Joi.object({ Joi.object({ get: Joi.object({ summary: Joi.string().required(), - description: Joi.string().required(), + description: Joi.string(), parameters: Joi.array() .items( Joi.object({ @@ -56,8 +56,12 @@ const serviceDefinition = Joi.object({ description: Joi.string(), in: Joi.string().valid('query', 'path').required(), required: Joi.boolean().required(), - schema: Joi.object({ type: Joi.string().required() }).required(), - example: Joi.string(), + schema: Joi.object({ + type: Joi.string().required(), + enum: Joi.array(), + }).required(), + allowEmptyValue: Joi.boolean(), + example: Joi.string().allow(null), }), ) .min(1) @@ -67,8 +71,8 @@ const serviceDefinition = Joi.object({ ), }).required() -function assertValidServiceDefinition(example, message = undefined) { - Joi.assert(example, serviceDefinition, message) +function assertValidServiceDefinition(service, message = undefined) { + Joi.assert(service, serviceDefinition, message) } const serviceDefinitionExport = Joi.object({ diff --git a/core/server/server.js b/core/server/server.js index 09b9112027007..5bf38ec3df3e2 100644 --- a/core/server/server.js +++ b/core/server/server.js @@ -362,20 +362,23 @@ class Server { }) if (!rasterUrl) { - camp.route(/^\/((?!img\/)).*\.png$/, (query, match, end, request) => { - makeSend( - 'svg', - request.res, - end, - )( - makeBadge({ - label: '404', - message: 'raster badges not available', - color: 'lightgray', - format: 'svg', - }), - ) - }) + camp.route( + /^\/((?!img|assets\/)).*\.png$/, + (query, match, end, request) => { + makeSend( + 'svg', + request.res, + end, + )( + makeBadge({ + label: '404', + message: 'raster badges not available', + color: 'lightgray', + format: 'svg', + }), + ) + }, + ) } camp.notfound(/(\.svg|\.json|)$/, (query, match, end, request) => { @@ -412,18 +415,21 @@ class Server { if (rasterUrl) { // Redirect to the raster server for raster versions of modern badges. - camp.route(/^\/((?!img\/)).*\.png$/, (queryParams, match, end, ask) => { - ask.res.statusCode = 301 - ask.res.setHeader( - 'Location', - rasterRedirectUrl({ rasterUrl }, ask.req.url), - ) - - const cacheDuration = (30 * 24 * 3600) | 0 // 30 days. - ask.res.setHeader('Cache-Control', `max-age=${cacheDuration}`) - - ask.res.end() - }) + camp.route( + /^\/((?!img|assets\/)).*\.png$/, + (queryParams, match, end, ask) => { + ask.res.statusCode = 301 + ask.res.setHeader( + 'Location', + rasterRedirectUrl({ rasterUrl }, ask.req.url), + ) + + const cacheDuration = (30 * 24 * 3600) | 0 // 30 days. + ask.res.setHeader('Cache-Control', `max-age=${cacheDuration}`) + + ask.res.end() + }, + ) } if (redirectUrl) { diff --git a/doc/logos.md b/doc/logos.md index 48617b7111d78..84915cb18fb01 100644 --- a/doc/logos.md +++ b/doc/logos.md @@ -1,38 +1,6 @@ # Logos -## Using Logos - -### SimpleIcons - -We support a wide range of logos via [SimpleIcons][]. They should be referenced by the logo slug e.g: - -![](https://img.shields.io/npm/v/npm.svg?logo=nodedotjs) - https://img.shields.io/npm/v/npm.svg?logo=nodedotjs - -The set of Simple Icon slugs can be found in the [slugs.md](https://github.com/simple-icons/simple-icons/blob/develop/slugs.md) file in the Simple Icons repository. NB - the Simple Icons site and that slugs.md page may at times contain new icons that haven't yet been pulled into the Shields.io runtime. More information on how and when we incorporate icon updates can be found [here](https://github.com/badges/shields/discussions/5369). - -### Shields logos - -We also maintain a small number of custom logos for some services: https://github.com/badges/shields/tree/master/logo They can also be referenced by name and take preference to SimpleIcons e.g: - -![](https://img.shields.io/npm/v/npm.svg?logo=npm) - https://img.shields.io/npm/v/npm.svg?logo=npm - -### Custom Logos - -Any custom logo can be passed in a URL parameter by base64 encoding it. e.g: - -![](https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+) - https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+ - -### logoColor parameter - -The `logoColor` param can be used to set the color of the logo. Hex, rgb, rgba, hsl, hsla and css named colors can all be used. For SimpleIcons named logos (which are monochrome), the color will be applied to the SimpleIcons logo. - -- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript -- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5 - -In the case where Shields hosts a custom multi-colored logo, if the `logoColor` param is passed, the corresponding SimpleIcons logo will be substituted and colored. - -- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab -- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white +For documentation on using logos, see https://shields.io/docs/logos ## Contributing Logos @@ -69,7 +37,6 @@ If you are submitting a pull request for a custom logo, please: We try to ensure our logos are compliant with brand guidelines. If one of our custom logos does not conform to the necessary brand guidelines, please open an issue on the [shields.io tracker](https://github.com/badges/shields/issues) and we'll work with you to resolve it. If a logo from the simple-icons set does not conform to the relevant brand guidelines, please open an issue on the [simple-icons tracker](https://github.com/simple-icons/simple-icons/issues) first. -[simpleicons]: https://simpleicons.org/ [simple-icons github]: https://github.com/simple-icons/simple-icons [svgo]: https://github.com/svg/svgo [svgomg]: https://jakearchibald.github.io/svgomg/ diff --git a/doc/static-badges.md b/doc/static-badges.md index 75d9df2f45c35..13ff0a86784c7 100644 --- a/doc/static-badges.md +++ b/doc/static-badges.md @@ -1,12 +1 @@ -# Static Badges - -It is possible to use shields.io to make a wide variety of badges displaying static text and/or logos. For example: - -- ![any text you like](https://img.shields.io/badge/any%20text-you%20like-blue) - https://img.shields.io/badge/any%20text-you%20like-blue -- ![just the message](https://img.shields.io/badge/just%20the%20message-8A2BE2) - https://img.shields.io/badge/just%20the%20message-8A2BE2 -- !['for the badge' style](https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge) - https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge -- ![with a logo](https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript) - https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript - -Full documentation of styles and parameters: https://shields.io/#styles - -More documentation on logos: https://github.com/badges/shields/blob/master/doc/logos.md +This documentation has moved to https://shields.io/docs/static-badges diff --git a/frontend/blog/2023-07-03-new-frontend.md b/frontend/blog/2023-07-03-new-frontend.md new file mode 100644 index 0000000000000..f5322b8abdae8 --- /dev/null +++ b/frontend/blog/2023-07-03-new-frontend.md @@ -0,0 +1,21 @@ +--- +slug: new-frontend +title: We launched a new frontend +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +Alongside the general visual refresh and improvements to look and feel, our new frontend has allowed us to address a number of long-standing feature requests and enhancements: + +- Clearer and more discoverable documentation for our [static](https://shields.io/badges/static-badge), dynamic [json](https://shields.io/badges/dynamic-json-badge)/[xml](https://shields.io/badges/dynamic-xml-badge)/[yaml](https://shields.io/badges/dynamic-yaml-badge) and [endpoint](https://shields.io/badges/endpoint-badge) badges +- Improved badge builder interface, with all optional query parameters included in the builder for each badge +- Each badge now has its own documentation page, which we can link to. e.g: [https://shields.io/badges/discord](https://shields.io/badges/discord) +- Light/dark mode themes +- Improved search +- Documentation for individual path and query parameters + +The new site also comes with big maintenance benefits for the core team. We rely heavily on [docusaurus](https://docusaurus.io/), [docusaurus-openapi](https://github.com/cloud-annotations/docusaurus-openapi), and [docusaurus-search-local](https://github.com/easyops-cn/docusaurus-search-local). This moves us to a mostly declarative setup, massively reducing the amount of custom frontend code we maintain ourselves. diff --git a/frontend/blog/2023-07-29-tag-filter.md b/frontend/blog/2023-07-29-tag-filter.md new file mode 100644 index 0000000000000..7bb863e2ef5ac --- /dev/null +++ b/frontend/blog/2023-07-29-tag-filter.md @@ -0,0 +1,19 @@ +--- +slug: tag-filter +title: Applying filters to GitHub Tag and Release badges +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +We recently shipped a feature which allows you to pass an arbitrary filter to the GitHub tag and release badges. The `filter` param can be used to apply a filter to the project's tag or release names before selecting the latest from the list. Two constructs are available: `*` is a wildcard matching zero or more characters, and if the pattern starts with a `!`, the whole pattern is negated. + +To give an example of how this might be useful, we create two types of tags on our GitHub repo: https://github.com/badges/shields/tags There are tags in the format `major.minor.patch` which correspond to our [NPM package releases](https://www.npmjs.com/package/badge-maker?activeTab=versions) and tags in the format `server-YYYY-MM-DD` that correspond to our [docker snapshot releases](https://registry.hub.docker.com/r/shieldsio/shields/tags?page=1&ordering=last_updated). + +In our case, this would allow us to make a badge that applies the filter `!server-*` to filter out the snapshot tags and just select the latest package tag. + +- ![tag badge without filter](https://img.shields.io/github/v/tag/badges/shields) - https://img.shields.io/github/v/tag/badges/shields +- ![tag badge with filter](https://img.shields.io/github/v/tag/badges/shields?filter=%21server-%2A) - https://img.shields.io/github/v/tag/badges/shields?filter=%21server-%2A diff --git a/frontend/docs/index.md b/frontend/docs/index.md new file mode 100644 index 0000000000000..11aa648de7f5b --- /dev/null +++ b/frontend/docs/index.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 1 +--- + +# Intro + +Shields.io is a service for concise, consistent, and legible badges, which can easily be included in GitHub readmes or any other web page. The service supports dozens of continuous integration services, package registries, distributions, app stores, social networks, code coverage services, and code analysis services. It is used by some of the world's most popular open-source projects. + +Browse a [complete list of badges](/badges) and locate a particular badge by using the search bar or by browsing the categories. + +Use the builder to fill in required path parameters for that badge type (like your username or repo) and optionally customize (label, colors etc.). And it's ready for use! Copy your badge url or code snippet, which can then be added to places like your GitHub readme files or other web pages. + +![screenshot of the badge builder](/img/builder.png) diff --git a/frontend/docs/intro.md b/frontend/docs/intro.md deleted file mode 100644 index fc44a306987f6..0000000000000 --- a/frontend/docs/intro.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -sidebar_position: 1 ---- - -# TODO diff --git a/frontend/docs/logos.md b/frontend/docs/logos.md new file mode 100644 index 0000000000000..0b43ef16091c5 --- /dev/null +++ b/frontend/docs/logos.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 2 +--- + +# Logos + +## SimpleIcons + +We support a wide range of logos via [SimpleIcons](https://simpleicons.org/). All simple-icons are referenced using icon slugs. e.g: + +![](https://img.shields.io/npm/v/npm.svg?logo=nodedotjs) - https://img.shields.io/npm/v/npm.svg?logo=nodedotjs + +You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository. NB - the Simple Icons site and slugs.md page may at times contain new icons that haven't yet been pulled into Shields.io yet. More information on how and when we incorporate icon updates can be found [here](https://github.com/badges/shields/discussions/5369). + +## Shields logos + +We also maintain a small number of custom logos for a handful of services: https://github.com/badges/shields/tree/master/logo They can also be referenced by name and take preference to SimpleIcons e.g: + +![](https://img.shields.io/npm/v/npm.svg?logo=npm) - https://img.shields.io/npm/v/npm.svg?logo=npm + +## Custom Logos + +Any custom logo can be passed in a URL parameter by base64 encoding it. e.g: + +![](https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+) - https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+ + +## logoColor parameter + +The `logoColor` param can be used to set the color of the logo. Hex, rgb, rgba, hsl, hsla and css named colors can all be used. For SimpleIcons named logos (which are monochrome), the color will be applied to the SimpleIcons logo. + +- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript +- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5 + +In the case where Shields hosts a custom multi-colored logo, if the `logoColor` param is passed, the corresponding SimpleIcons logo will be substituted and colored. + +- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab +- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white diff --git a/frontend/docs/static-badges.md b/frontend/docs/static-badges.md new file mode 100644 index 0000000000000..56a72f1317fad --- /dev/null +++ b/frontend/docs/static-badges.md @@ -0,0 +1,13 @@ +# Static Badges + +It is possible to use shields.io to make a wide variety of badges displaying static text and/or logos. For example: + +- ![any text you like](https://img.shields.io/badge/any%20text-you%20like-blue) - https://img.shields.io/badge/any%20text-you%20like-blue +- ![just the message](https://img.shields.io/badge/just%20the%20message-8A2BE2) - https://img.shields.io/badge/just%20the%20message-8A2BE2 +- !['for the badge' style](https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge) - https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge +- ![with a logo](https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript) - https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript + +For more info, see: + +- [Static badge builder](/badges/static-badge), including full documentation of styles and parameters +- [Logos](/docs/logos) diff --git a/frontend/docusaurus.config.cjs b/frontend/docusaurus.config.cjs index 4169b36b96809..6a6bc2c98805b 100644 --- a/frontend/docusaurus.config.cjs +++ b/frontend/docusaurus.config.cjs @@ -31,11 +31,11 @@ const config = { ({ docs: { sidebarPath: require.resolve('./sidebars.cjs'), - editUrl: 'https://github.com/badges/shields/', + editUrl: 'https://github.com/badges/shields/tree/master/frontend', }, blog: { showReadingTime: true, - editUrl: 'https://github.com/badges/shields/', + editUrl: 'https://github.com/badges/shields/tree/master/frontend', }, theme: { customCss: require.resolve('./src/css/custom.css'), @@ -60,7 +60,13 @@ const config = { }, items: [ { to: '/badges', label: 'Badges', position: 'left' }, + { + to: '/docs', + label: 'Documentation', + position: 'left', + }, { to: '/community', label: 'Community', position: 'left' }, + { to: '/blog', label: 'Blog', position: 'left' }, { href: 'https://github.com/badges/shields', label: 'GitHub', diff --git a/frontend/src/pages/index.js b/frontend/src/pages/index.js index 5ed42e413cefa..63c213f0cea04 100644 --- a/frontend/src/pages/index.js +++ b/frontend/src/pages/index.js @@ -28,7 +28,7 @@ export default function Home() { return (
diff --git a/frontend/static/img/builder.png b/frontend/static/img/builder.png new file mode 100644 index 0000000000000..1196e758f67e4 Binary files /dev/null and b/frontend/static/img/builder.png differ diff --git a/package-lock.json b/package-lock.json index 15bb2b10c6bba..02bb904091fd8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,9 @@ "dependencies": { "@renovate/pep440": "^1.0.0", "@renovatebot/ruby-semver": "^2.1.11", - "@sentry/node": "^7.58.1", + "@sentry/node": "^7.61.1", "@shields_io/camp": "^18.1.2", - "@xmldom/xmldom": "0.8.9", + "@xmldom/xmldom": "0.8.10", "badge-maker": "file:badge-maker", "bytes": "^3.1.2", "camelcase": "^7.0.1", @@ -26,7 +26,7 @@ "decamelize": "^3.2.0", "emojic": "^1.1.17", "escape-string-regexp": "^4.0.0", - "fast-xml-parser": "^4.2.5", + "fast-xml-parser": "^4.2.7", "glob": "^10.3.3", "global-agent": "^3.0.0", "got": "^13.0.0", @@ -44,14 +44,14 @@ "node-pg-migrate": "^6.2.2", "parse-link-header": "^2.0.0", "path-to-regexp": "^6.2.1", - "pg": "^8.11.1", - "pretty-bytes": "^6.1.0", + "pg": "^8.11.2", + "pretty-bytes": "^6.1.1", "priorityqueuejs": "^2.0.0", "prom-client": "^14.2.0", "qs": "^6.11.2", "query-string": "^8.1.0", "semver": "~7.5.4", - "simple-icons": "9.5.0", + "simple-icons": "9.8.0", "webextension-store-meta": "^1.0.5", "xpath": "~0.0.33" }, @@ -59,36 +59,36 @@ "@docusaurus/core": "^2.0.0", "@easyops-cn/docusaurus-search-local": "^0.35.0", "@mdx-js/react": "^1.6.21", - "@typescript-eslint/parser": "^6.0.0", - "c8": "^8.0.0", + "@typescript-eslint/parser": "^6.2.1", + "c8": "^8.0.1", "caller": "^1.1.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", "chai-datetime": "^1.8.0", "chai-string": "^1.4.0", "child-process-promise": "^2.2.1", - "clsx": "^1.1.1", + "clsx": "^2.0.0", "concurrently": "^8.2.0", - "cypress": "^12.17.1", + "cypress": "^12.17.3", "cypress-wait-for-stable-dom": "^0.1.0", - "danger": "^11.2.6", + "danger": "^11.2.8", "deepmerge": "^4.3.1", "docusaurus-preset-openapi": "0.6.4", - "eslint": "8.44.0", - "eslint-config-prettier": "^8.8.0", + "eslint": "8.46.0", + "eslint-config-prettier": "^9.0.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": "^0.7.2", "eslint-plugin-cypress": "^2.13.3", "eslint-plugin-icedfrisby": "^0.1.0", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jsdoc": "^46.4.3", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-jsdoc": "^46.4.5", "eslint-plugin-mocha": "^10.1.0", "eslint-plugin-no-extension-in-require": "^0.2.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "6.1.1", - "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react": "^7.33.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-sort-class-members": "^1.18.0", "form-data": "^4.0.0", @@ -109,14 +109,14 @@ "npm-run-all": "^4.1.5", "open-cli": "^7.2.0", "portfinder": "^1.0.32", - "prettier": "3.0.0", + "prettier": "3.0.1", "prism-react-renderer": "^2.0.6", "react": "^17.0.2", "react-dom": "^17.0.2", "read-all-stdin-sync": "^1.0.5", "rimraf": "^5.0.1", "sazerac": "^2.0.0", - "simple-git-hooks": "^2.8.1", + "simple-git-hooks": "^2.9.0", "sinon": "^15.2.0", "sinon-chai": "^3.7.0", "snap-shot-it": "^7.9.10", @@ -3034,6 +3034,15 @@ "react-dom": "^16.8.4 || ^17.0.0" } }, + "node_modules/@docusaurus/theme-classic/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@docusaurus/theme-classic/node_modules/prism-react-renderer": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", @@ -3074,6 +3083,15 @@ "react-dom": "^16.8.4 || ^17.0.0" } }, + "node_modules/@docusaurus/theme-common/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@docusaurus/theme-common/node_modules/prism-react-renderer": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", @@ -3114,6 +3132,15 @@ "react-dom": "^16.8.4 || ^17.0.0" } }, + "node_modules/@docusaurus/theme-search-algolia/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@docusaurus/theme-translations": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-2.4.1.tgz", @@ -3260,6 +3287,15 @@ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@es-joy/jsdoccomment": { "version": "0.39.4", "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", @@ -3290,18 +3326,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", + "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -3349,9 +3385,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", + "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4590,13 +4626,13 @@ } }, "node_modules/@sentry-internal/tracing": { - "version": "7.58.1", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.58.1.tgz", - "integrity": "sha512-kOWKqyjYdDgvO6CacXneE9UrFQHT3BXF1UpCAlnHchW/TqRFmg89sJAEUjEPGzN7y6IaX1G4j2dBPDE0OFQi3w==", + "version": "7.61.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.61.1.tgz", + "integrity": "sha512-E8J6ZMXHGdWdmgKBK/ounuUppDK65c4Hphin6iVckDGMEATn0auYAKngeyRUMLof1167DssD8wxcIA4aBvmScA==", "dependencies": { - "@sentry/core": "7.58.1", - "@sentry/types": "7.58.1", - "@sentry/utils": "7.58.1", + "@sentry/core": "7.61.1", + "@sentry/types": "7.61.1", + "@sentry/utils": "7.61.1", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -4604,12 +4640,12 @@ } }, "node_modules/@sentry/core": { - "version": "7.58.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.58.1.tgz", - "integrity": "sha512-hpeB5fZ5T6Jg1CBqz486jHgWuJ5R/HD0wyYX+S3LDDsHCJo6V3TxNuoxYDlTTerRRfZdTwr9GYJXskehpU26IA==", + "version": "7.61.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.61.1.tgz", + "integrity": "sha512-WTRt0J33KhUbYuDQZ5G58kdsNeQ5JYrpi6o+Qz+1xTv60DQq/tBGRJ7d86SkmdnGIiTs6W1hsxAtyiLS0y9d2A==", "dependencies": { - "@sentry/types": "7.58.1", - "@sentry/utils": "7.58.1", + "@sentry/types": "7.61.1", + "@sentry/utils": "7.61.1", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -4617,14 +4653,14 @@ } }, "node_modules/@sentry/node": { - "version": "7.58.1", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.58.1.tgz", - "integrity": "sha512-XsSu0xg5SYcltMbatnRBcIZw9pXwGJoGbYDLuPhhuqBz2mnQ0mQ9Try9dn/agDU7KZzT0IyA1qkPXk0NkMe3rw==", - "dependencies": { - "@sentry-internal/tracing": "7.58.1", - "@sentry/core": "7.58.1", - "@sentry/types": "7.58.1", - "@sentry/utils": "7.58.1", + "version": "7.61.1", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.61.1.tgz", + "integrity": "sha512-+crVAeymXdWZcDuwU9xySf4sVv2fHOFlr13XqeXl73q4zqKJM1IX4VUO9On3+jTyGfB5SCAuBBYpzA3ehBfeYw==", + "dependencies": { + "@sentry-internal/tracing": "7.61.1", + "@sentry/core": "7.61.1", + "@sentry/types": "7.61.1", + "@sentry/utils": "7.61.1", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -4635,19 +4671,19 @@ } }, "node_modules/@sentry/types": { - "version": "7.58.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.58.1.tgz", - "integrity": "sha512-OnKG+yrilPBeVNQK3biF0u/4IDjwH+boJU1XzJOnYdMRO8uzTWxvaRqpt0C8sVE9VAetRi2eutkzOgCXZISRrw==", + "version": "7.61.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.61.1.tgz", + "integrity": "sha512-CpPKL+OfwYOduRX9AT3p+Ie1fftgcCPd5WofTVVq7xeWRuerOOf2iJd0v+8yHQ25omgres1YOttDkCcvQRn4Jw==", "engines": { "node": ">=8" } }, "node_modules/@sentry/utils": { - "version": "7.58.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.58.1.tgz", - "integrity": "sha512-iC9xZJBHp4+MDrZjKwcmMUhI5sTmpUcttwmsJL9HA6ACW+L1XX2eGSDky5pSlhhVFR7q7jJnQ7YUlMQ/jcd8eQ==", + "version": "7.61.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.61.1.tgz", + "integrity": "sha512-pUPXoiuYrTEPcBHjRizFB6eZEGm/6cTBwdWSHUjkGKvt19zuZ1ixFJQV6LrIL/AMeiQbmfQ+kTd/8SR7E9rcTQ==", "dependencies": { - "@sentry/types": "7.58.1", + "@sentry/types": "7.61.1", "tslib": "^2.4.1 || ^1.9.3" }, "engines": { @@ -5309,9 +5345,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "14.18.49", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.49.tgz", - "integrity": "sha512-bJhkIh+GsIpLIJ/QlMYI30O1ZGDeUq+8S9uCq2fR3EYPvpnXtU0KsDJcljONyvyowjmYCQ3BxJje2CMXzCCvSg==" + "version": "16.18.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.39.tgz", + "integrity": "sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -5541,15 +5577,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.0.0.tgz", - "integrity": "sha512-TNaufYSPrr1U8n+3xN+Yp9g31vQDJqhXzzPSHfQDLcaO4tU+mCfODPxCwf4H530zo7aUBE3QIdxCXamEnG04Tg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.1.tgz", + "integrity": "sha512-Ld+uL1kYFU8e6btqBFpsHkwQ35rw30IWpdQxgOqOh4NfxSDH6uCkah1ks8R/RgQqI5hHPXMaLy9fbFseIe+dIg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.0.0", - "@typescript-eslint/types": "6.0.0", - "@typescript-eslint/typescript-estree": "6.0.0", - "@typescript-eslint/visitor-keys": "6.0.0", + "@typescript-eslint/scope-manager": "6.2.1", + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/typescript-estree": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1", "debug": "^4.3.4" }, "engines": { @@ -5569,13 +5605,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.0.0.tgz", - "integrity": "sha512-o4q0KHlgCZTqjuaZ25nw5W57NeykZT9LiMEG4do/ovwvOcPnDO1BI5BQdCsUkjxFyrCL0cSzLjvIMfR9uo7cWg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.1.tgz", + "integrity": "sha512-UCqBF9WFqv64xNsIEPfBtenbfodPXsJ3nPAr55mGPkQIkiQvgoWNo+astj9ZUfJfVKiYgAZDMnM6dIpsxUMp3Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.0.0", - "@typescript-eslint/visitor-keys": "6.0.0" + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5586,9 +5622,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.0.0.tgz", - "integrity": "sha512-Zk9KDggyZM6tj0AJWYYKgF0yQyrcnievdhG0g5FqyU3Y2DRxJn4yWY21sJC0QKBckbsdKKjYDV2yVrrEvuTgxg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.1.tgz", + "integrity": "sha512-528bGcoelrpw+sETlyM91k51Arl2ajbNT9L4JwoXE2dvRe1yd8Q64E4OL7vHYw31mlnVsf+BeeLyAZUEQtqahQ==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -5599,17 +5635,17 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.0.0.tgz", - "integrity": "sha512-2zq4O7P6YCQADfmJ5OTDQTP3ktajnXIRrYAtHM9ofto/CJZV3QfJ89GEaM2BNGeSr1KgmBuLhEkz5FBkS2RQhQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.1.tgz", + "integrity": "sha512-G+UJeQx9AKBHRQBpmvr8T/3K5bJa485eu+4tQBxFq0KoT22+jJyzo1B50JDT9QdC1DEmWQfdKsa8ybiNWYsi0Q==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.0.0", - "@typescript-eslint/visitor-keys": "6.0.0", + "@typescript-eslint/types": "6.2.1", + "@typescript-eslint/visitor-keys": "6.2.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.5.0", + "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "engines": { @@ -5626,12 +5662,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.0.0.tgz", - "integrity": "sha512-cvJ63l8c0yXdeT5POHpL0Q1cZoRcmRKFCtSjNGJxPkcP571EfZMcNbzWAc7oK3D1dRzm/V5EwtkANTZxqvuuUA==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.1.tgz", + "integrity": "sha512-iTN6w3k2JEZ7cyVdZJTVJx2Lv7t6zFA8DCrJEHD2mwfc16AEvvBWVhbFh34XyG2NORCd0viIgQY1+u7kPI0WpA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.0.0", + "@typescript-eslint/types": "6.2.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -5789,9 +5825,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.9.tgz", - "integrity": "sha512-4VSbbcMoxc4KLjb1gs96SRmi7w4h1SF+fCoiK0XaQX62buCc1G5d0DC5bJ9xJBNPDSVCmIrcl8BiYxzjrqaaJA==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "engines": { "node": ">=10.0.0" } @@ -6215,6 +6251,25 @@ "node": ">=0.10.0" } }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.2.tgz", + "integrity": "sha512-tb5thFFlUcp7NdNF6/MpDk/1r/4awWG1FIz3YqDf+/zJSTezBb+/5WViH41obXULHVpDzoiCLpJ/ZO9YbJMsdw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", @@ -7046,9 +7101,9 @@ } }, "node_modules/c8": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.0.tgz", - "integrity": "sha512-XHA5vSfCLglAc0Xt8eLBZMv19lgiBSjnb1FLAQgnwkuhJYEonpilhEB4Ea3jPAbm0FhD6VVJrc0z73jPe7JyGQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/c8/-/c8-8.0.1.tgz", + "integrity": "sha512-EINpopxZNH1mETuI0DzRA4MZpAUH+IFiRhnmFD3vFr3vdrgxqi3VfE3KL0AIL+zDq8rC9bZqwM/VDmmoe04y7w==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", @@ -7056,13 +7111,13 @@ "find-up": "^5.0.0", "foreground-child": "^2.0.0", "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-reports": "^3.1.4", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", "rimraf": "^3.0.2", "test-exclude": "^6.0.0", "v8-to-istanbul": "^9.0.0", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9" + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" }, "bin": { "c8": "bin/c8.js" @@ -7071,6 +7126,59 @@ "node": ">=12" } }, + "node_modules/c8/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/c8/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/c8/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/c8/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/c8/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -7091,6 +7199,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/c8/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/c8/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -7106,6 +7223,64 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/c8/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/cacheable-lookup": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", @@ -7882,9 +8057,9 @@ } }, "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", "dev": true, "engines": { "node": ">=6" @@ -9015,15 +9190,15 @@ "dev": true }, "node_modules/cypress": { - "version": "12.17.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.1.tgz", - "integrity": "sha512-eKfBgO6t8waEyhegL4gxD7tcI6uTCGttu+ZU7y9Hq8BlpMztd7iLeIF4AJFAnbZH1xjX+wwgg4cRKFNSvv3VWQ==", + "version": "12.17.3", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.17.3.tgz", + "integrity": "sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==", "dev": true, "hasInstallScript": true, "dependencies": { "@cypress/request": "^2.88.11", "@cypress/xvfb": "^1.2.4", - "@types/node": "^14.14.31", + "@types/node": "^16.18.39", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", @@ -9175,9 +9350,9 @@ } }, "node_modules/danger": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/danger/-/danger-11.2.6.tgz", - "integrity": "sha512-EEeuDmUcxPGJ166q7Zzz1WEiV+e0qbPopaX4sXxds8U5doGMdw/8oOUOVye7JiHIBuss3KvQWt4YHZeD3jSCfw==", + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/danger/-/danger-11.2.8.tgz", + "integrity": "sha512-d3iYhIJmo3V5WatWjsHbFpx/V5nz7fKsM7rQi91f/9CemLCH8wt3Jg1JKsEpiTHUtzNplOpudk0yFsWeHygd/w==", "dev": true, "dependencies": { "@gitbeaker/node": "^21.3.0", @@ -10093,6 +10268,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/docusaurus-plugin-openapi/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/docusaurus-plugin-openapi/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -10224,6 +10408,15 @@ "ieee754": "^1.2.1" } }, + "node_modules/docusaurus-theme-openapi/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/docusaurus-theme-openapi/node_modules/prism-react-renderer": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz", @@ -10827,27 +11020,27 @@ } }, "node_modules/eslint": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", - "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", + "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.1", + "@eslint/js": "^8.46.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.2", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -10857,7 +11050,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -10869,7 +11061,6 @@ "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -10883,9 +11074,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", + "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -11222,26 +11413,29 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", + "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", "dev": true, "dependencies": { "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", "array.prototype.flat": "^1.3.1", "array.prototype.flatmap": "^1.3.1", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", + "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.11.0", + "is-core-module": "^2.12.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "resolve": "^1.22.3", + "semver": "^6.3.1", + "tsconfig-paths": "^3.14.2" }, "engines": { "node": ">=4" @@ -11272,18 +11466,18 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/eslint-plugin-jsdoc": { - "version": "46.4.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.3.tgz", - "integrity": "sha512-Prc7ol+vCIghPeECpwZq5+P+VZfoi87suywvbYCiCnkI1kTmVSdcOC2M8mioglWxBbd28wbb1OVjg/8OzGzatA==", + "version": "46.4.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.5.tgz", + "integrity": "sha512-HjTuxqDYplAQFu29F3MHFCDDBgeqOxPXI6TyBhL0u2rr4XntJ0z3C9PmJvpjFscKdHwkZDN/0l1QCG0QwyRi4g==", "dev": true, "dependencies": { "@es-joy/jsdoccomment": "~0.39.4", @@ -11293,7 +11487,7 @@ "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", "is-builtin-module": "^3.2.1", - "semver": "^7.5.1", + "semver": "^7.5.4", "spdx-expression-parse": "^3.0.1" }, "engines": { @@ -11423,9 +11617,9 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "version": "7.33.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.1.tgz", + "integrity": "sha512-L093k0WAMvr6VhNwReB8VgOq5s2LesZmrpPdKz/kZElQDzqS7G7+DnKoqT+w4JwuiGeAhAvHO0fvy0Eyk4ejDA==", "dev": true, "dependencies": { "array-includes": "^3.1.6", @@ -11441,7 +11635,7 @@ "object.values": "^1.1.6", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", + "semver": "^6.3.1", "string.prototype.matchall": "^4.0.8" }, "engines": { @@ -11493,9 +11687,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -11566,9 +11760,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", + "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -11641,9 +11835,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -11723,9 +11917,9 @@ } }, "node_modules/espree": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", - "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { "acorn": "^8.9.0", @@ -12154,9 +12348,9 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz", + "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==", "funding": [ { "type": "paypal", @@ -15047,17 +15241,32 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-report/node_modules/supports-color": { @@ -15073,9 +15282,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -18566,6 +18775,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.groupby": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.0.tgz", + "integrity": "sha512-70MWG6NfRH9GnbZOikuhPPYzpUpof9iW2J9E4dW7FXTqPNb6rllE6u39SKwwiNh8lCwX3DDb5OgcKGiEBrTTyw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "get-intrinsic": "^1.2.1" + } + }, "node_modules/object.hasown": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", @@ -19568,13 +19789,13 @@ "dev": true }, "node_modules/pg": { - "version": "8.11.1", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.1.tgz", - "integrity": "sha512-utdq2obft07MxaDg0zBJI+l/M3mBRfIpEN3iSemsz0G5F2/VXx+XzqF4oxrbIZXQxt2AZzIUzyVg/YM6xOP/WQ==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.2.tgz", + "integrity": "sha512-l4rmVeV8qTIrrPrIR3kZQqBgSN93331s9i6wiUiLOSk0Q7PmUxZD/m1rQI622l3NfqBby9Ar5PABfS/SulfieQ==", "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.1", + "pg-connection-string": "^2.6.2", "pg-pool": "^3.6.1", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", @@ -19602,9 +19823,9 @@ "optional": true }, "node_modules/pg-connection-string": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.1.tgz", - "integrity": "sha512-w6ZzNu6oMmIzEAYVw+RLK0+nqHPt8K3ZnknKi+g48Ak2pr3dtljJW3o+D/n2zzCG07Zoe9VOX3aiKpj+BN0pjg==" + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, "node_modules/pg-int8": { "version": "1.0.1", @@ -20845,9 +21066,9 @@ } }, "node_modules/prettier": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.0.tgz", - "integrity": "sha512-zBf5eHpwHOGPC47h0zrPyNn+eAEIdEzfywMoYn2XPi0P44Zp0tSq64rq0xAREh4auw2cJZHo9QUob+NqCQky4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz", + "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -20860,9 +21081,9 @@ } }, "node_modules/pretty-bytes": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.0.tgz", - "integrity": "sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "engines": { "node": "^14.13.1 || >=16.0.0" }, @@ -20952,6 +21173,15 @@ "react": ">=16.0.0" } }, + "node_modules/prism-react-renderer/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -22838,12 +23068,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.3.tgz", + "integrity": "sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.12.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -23812,9 +24042,9 @@ "dev": true }, "node_modules/simple-git-hooks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.8.1.tgz", - "integrity": "sha512-DYpcVR1AGtSfFUNzlBdHrQGPsOhuuEJ/FkmPOOlFysP60AHd3nsEpkGq/QEOdtUyT1Qhk7w9oLmFoMG+75BDog==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/simple-git-hooks/-/simple-git-hooks-2.9.0.tgz", + "integrity": "sha512-waSQ5paUQtyGC0ZxlHmcMmD9I1rRXauikBwX31bX58l5vTOhCEcBC5Bi+ZDkPXTjDnZAF8TbCqKBY+9+sVPScw==", "dev": true, "hasInstallScript": true, "bin": { @@ -23822,9 +24052,9 @@ } }, "node_modules/simple-icons": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-9.5.0.tgz", - "integrity": "sha512-FEtVYBJgsQb+Oz2f6DBbPipOl/9QVAEPvxZj1bllsoRsYL2LfufvhPDSLUdmpClUUDW5UjphBoEwBQvqJ1p3lg==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/simple-icons/-/simple-icons-9.8.0.tgz", + "integrity": "sha512-xVHfr+souquvsKoqYzfgEoQeZSt1i8ka9botwc81wGZ9VNOc2xr96GPz1ziycpX+vHLuZFlgibYcqCzJZeCPfg==", "engines": { "node": ">=0.12.18" }, @@ -27794,9 +28024,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "engines": { "node": ">=0.10.0" } diff --git a/package.json b/package.json index 133399b342e49..3cc5e9ac61adc 100644 --- a/package.json +++ b/package.json @@ -23,9 +23,9 @@ "dependencies": { "@renovate/pep440": "^1.0.0", "@renovatebot/ruby-semver": "^2.1.11", - "@sentry/node": "^7.58.1", + "@sentry/node": "^7.61.1", "@shields_io/camp": "^18.1.2", - "@xmldom/xmldom": "0.8.9", + "@xmldom/xmldom": "0.8.10", "badge-maker": "file:badge-maker", "bytes": "^3.1.2", "camelcase": "^7.0.1", @@ -38,7 +38,7 @@ "decamelize": "^3.2.0", "emojic": "^1.1.17", "escape-string-regexp": "^4.0.0", - "fast-xml-parser": "^4.2.5", + "fast-xml-parser": "^4.2.7", "glob": "^10.3.3", "global-agent": "^3.0.0", "got": "^13.0.0", @@ -56,14 +56,14 @@ "node-pg-migrate": "^6.2.2", "parse-link-header": "^2.0.0", "path-to-regexp": "^6.2.1", - "pg": "^8.11.1", - "pretty-bytes": "^6.1.0", + "pg": "^8.11.2", + "pretty-bytes": "^6.1.1", "priorityqueuejs": "^2.0.0", "prom-client": "^14.2.0", "qs": "^6.11.2", "query-string": "^8.1.0", "semver": "~7.5.4", - "simple-icons": "9.5.0", + "simple-icons": "9.8.0", "webextension-store-meta": "^1.0.5", "xpath": "~0.0.33" }, @@ -146,36 +146,36 @@ "@docusaurus/core": "^2.0.0", "@easyops-cn/docusaurus-search-local": "^0.35.0", "@mdx-js/react": "^1.6.21", - "@typescript-eslint/parser": "^6.0.0", - "c8": "^8.0.0", + "@typescript-eslint/parser": "^6.2.1", + "c8": "^8.0.1", "caller": "^1.1.0", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", "chai-datetime": "^1.8.0", "chai-string": "^1.4.0", "child-process-promise": "^2.2.1", - "clsx": "^1.1.1", + "clsx": "^2.0.0", "concurrently": "^8.2.0", - "cypress": "^12.17.1", + "cypress": "^12.17.3", "cypress-wait-for-stable-dom": "^0.1.0", - "danger": "^11.2.6", + "danger": "^11.2.8", "deepmerge": "^4.3.1", "docusaurus-preset-openapi": "0.6.4", - "eslint": "8.44.0", - "eslint-config-prettier": "^8.8.0", + "eslint": "8.46.0", + "eslint-config-prettier": "^9.0.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": "^0.7.2", "eslint-plugin-cypress": "^2.13.3", "eslint-plugin-icedfrisby": "^0.1.0", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jsdoc": "^46.4.3", + "eslint-plugin-import": "^2.28.0", + "eslint-plugin-jsdoc": "^46.4.5", "eslint-plugin-mocha": "^10.1.0", "eslint-plugin-no-extension-in-require": "^0.2.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "6.1.1", - "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react": "^7.33.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-sort-class-members": "^1.18.0", "form-data": "^4.0.0", @@ -196,14 +196,14 @@ "npm-run-all": "^4.1.5", "open-cli": "^7.2.0", "portfinder": "^1.0.32", - "prettier": "3.0.0", + "prettier": "3.0.1", "prism-react-renderer": "^2.0.6", "react": "^17.0.2", "react-dom": "^17.0.2", "read-all-stdin-sync": "^1.0.5", "rimraf": "^5.0.1", "sazerac": "^2.0.0", - "simple-git-hooks": "^2.8.1", + "simple-git-hooks": "^2.9.0", "sinon": "^15.2.0", "sinon-chai": "^3.7.0", "snap-shot-it": "^7.9.10", diff --git a/services/amo/amo-downloads.service.js b/services/amo/amo-downloads.service.js index ae61a534e0106..d8ca97447c50d 100644 --- a/services/amo/amo-downloads.service.js +++ b/services/amo/amo-downloads.service.js @@ -1,8 +1,8 @@ import { renderDownloadsBadge } from '../downloads.js' -import { redirector } from '../index.js' -import { BaseAmoService, keywords } from './amo-base.js' +import { redirector, pathParams } from '../index.js' +import { BaseAmoService } from './amo-base.js' -const documentation = ` +const description = ` Previously \`amo/d\` provided a “total downloads” badge. However, [updates to the v3 API](https://github.com/badges/shields/issues/3079) only give us weekly downloads. The route \`amo/d\` redirects to \`amo/dw\`. @@ -12,15 +12,15 @@ class AmoWeeklyDownloads extends BaseAmoService { static category = 'downloads' static route = { base: 'amo/dw', pattern: ':addonId' } - static examples = [ - { - title: 'Mozilla Add-on', - namedParams: { addonId: 'dustman' }, - staticPreview: this.render({ downloads: 120 }), - keywords, - documentation, + static openApi = { + '/amo/dw/{addonId}': { + get: { + summary: 'Mozilla Add-on Downloads', + description, + parameters: pathParams({ name: 'addonId', example: 'dustman' }), + }, }, - ] + } static _cacheLength = 21600 diff --git a/services/amo/amo-rating.service.js b/services/amo/amo-rating.service.js index 57ad89be902ab..5930c1dd0f38b 100644 --- a/services/amo/amo-rating.service.js +++ b/services/amo/amo-rating.service.js @@ -1,27 +1,26 @@ import { starRating } from '../text-formatters.js' import { floorCount as floorCountColor } from '../color-formatters.js' -import { BaseAmoService, keywords } from './amo-base.js' +import { pathParams } from '../index.js' +import { BaseAmoService } from './amo-base.js' export default class AmoRating extends BaseAmoService { static category = 'rating' static route = { base: 'amo', pattern: ':format(stars|rating)/:addonId' } - static examples = [ - { - title: 'Mozilla Add-on', - pattern: 'rating/:addonId', - namedParams: { addonId: 'dustman' }, - staticPreview: this.render({ format: 'rating', rating: 4 }), - keywords, + static openApi = { + '/amo/rating/{addonId}': { + get: { + summary: 'Mozilla Add-on Rating', + parameters: pathParams({ name: 'addonId', example: 'dustman' }), + }, }, - { - title: 'Mozilla Add-on', - pattern: 'stars/:addonId', - namedParams: { addonId: 'dustman' }, - staticPreview: this.render({ format: 'stars', rating: 4 }), - keywords, + '/amo/stars/{addonId}': { + get: { + summary: 'Mozilla Add-on Stars', + parameters: pathParams({ name: 'addonId', example: 'dustman' }), + }, }, - ] + } static _cacheLength = 7200 diff --git a/services/amo/amo-users.service.js b/services/amo/amo-users.service.js index 0a841525bbb21..0a4b4be526dd7 100644 --- a/services/amo/amo-users.service.js +++ b/services/amo/amo-users.service.js @@ -1,18 +1,19 @@ import { renderDownloadsBadge } from '../downloads.js' -import { BaseAmoService, keywords } from './amo-base.js' +import { pathParams } from '../index.js' +import { BaseAmoService } from './amo-base.js' export default class AmoUsers extends BaseAmoService { static category = 'downloads' static route = { base: 'amo/users', pattern: ':addonId' } - static examples = [ - { - title: 'Mozilla Add-on', - namedParams: { addonId: 'dustman' }, - staticPreview: this.render({ users: 750 }), - keywords, + static openApi = { + '/amo/users/{addonId}': { + get: { + summary: 'Mozilla Add-on Users', + parameters: pathParams({ name: 'addonId', example: 'dustman' }), + }, }, - ] + } static _cacheLength = 21600 diff --git a/services/amo/amo-version.service.js b/services/amo/amo-version.service.js index 96394bba7fc44..219692437d0d4 100644 --- a/services/amo/amo-version.service.js +++ b/services/amo/amo-version.service.js @@ -1,18 +1,19 @@ import { renderVersionBadge } from '../version.js' -import { BaseAmoService, keywords } from './amo-base.js' +import { pathParams } from '../index.js' +import { BaseAmoService } from './amo-base.js' export default class AmoVersion extends BaseAmoService { static category = 'version' static route = { base: 'amo/v', pattern: ':addonId' } - static examples = [ - { - title: 'Mozilla Add-on', - namedParams: { addonId: 'dustman' }, - staticPreview: renderVersionBadge({ version: '2.1.0' }), - keywords, + static openApi = { + '/amo/v/{addonId}': { + get: { + summary: 'Mozilla Add-on Version', + parameters: pathParams({ name: 'addonId', example: 'dustman' }), + }, }, - ] + } async handle({ addonId }) { const data = await this.fetch({ addonId }) diff --git a/services/ansible/ansible-collection.service.js b/services/ansible/ansible-collection.service.js index ae7cb07062970..131f86206eac8 100644 --- a/services/ansible/ansible-collection.service.js +++ b/services/ansible/ansible-collection.service.js @@ -1,5 +1,5 @@ import Joi from 'joi' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const ansibleCollectionSchema = Joi.object({ name: Joi.string().required(), @@ -12,15 +12,14 @@ class AnsibleGalaxyCollectionName extends BaseJsonService { static category = 'other' static route = { base: 'ansible/collection', pattern: ':collectionId' } - static examples = [ - { - title: 'Ansible Collection', - namedParams: { collectionId: '278' }, - staticPreview: this.render({ - name: 'community.general', - }), + static openApi = { + '/ansible/collection/{collectionId}': { + get: { + summary: 'Ansible Collection', + parameters: pathParams({ name: 'collectionId', example: '278' }), + }, }, - ] + } static defaultBadgeData = { label: 'collection' } diff --git a/services/ansible/ansible-quality.service.js b/services/ansible/ansible-quality.service.js index 6af7ae16ca588..249d456e04fa1 100644 --- a/services/ansible/ansible-quality.service.js +++ b/services/ansible/ansible-quality.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { floorCount } from '../color-formatters.js' -import { BaseJsonService, InvalidResponse } from '../index.js' +import { BaseJsonService, InvalidResponse, pathParams } from '../index.js' const ansibleContentSchema = Joi.object({ quality_score: Joi.number().allow(null).required(), @@ -20,15 +20,14 @@ export default class AnsibleGalaxyContentQualityScore extends AnsibleGalaxyConte static category = 'analysis' static route = { base: 'ansible/quality', pattern: ':projectId' } - static examples = [ - { - title: 'Ansible Quality Score', - namedParams: { - projectId: '432', + static openApi = { + '/ansible/quality/{projectId}': { + get: { + summary: 'Ansible Quality Score', + parameters: pathParams({ name: 'projectId', example: '432' }), }, - staticPreview: this.render({ qualityScore: 4.125 }), }, - ] + } static defaultBadgeData = { label: 'quality' } diff --git a/services/ansible/ansible-role.service.js b/services/ansible/ansible-role.service.js index ffc27193dc13d..acf5ccce42bee 100644 --- a/services/ansible/ansible-role.service.js +++ b/services/ansible/ansible-role.service.js @@ -1,7 +1,7 @@ import Joi from 'joi' import { renderDownloadsBadge } from '../downloads.js' import { nonNegativeInteger } from '../validators.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const ansibleRoleSchema = Joi.object({ download_count: nonNegativeInteger, @@ -27,13 +27,14 @@ class AnsibleGalaxyRoleDownloads extends AnsibleGalaxyRole { static category = 'downloads' static route = { base: 'ansible/role/d', pattern: ':roleId' } - static examples = [ - { - title: 'Ansible Role', - namedParams: { roleId: '3078' }, - staticPreview: renderDownloadsBadge({ downloads: 76 }), + static openApi = { + '/ansible/role/d/{roleId}': { + get: { + summary: 'Ansible Role', + parameters: pathParams({ name: 'roleId', example: '3078' }), + }, }, - ] + } static defaultBadgeData = { label: 'role downloads' } @@ -47,15 +48,14 @@ class AnsibleGalaxyRoleName extends AnsibleGalaxyRole { static category = 'other' static route = { base: 'ansible/role', pattern: ':roleId' } - static examples = [ - { - title: 'Ansible Role', - namedParams: { roleId: '3078' }, - staticPreview: this.render({ - name: 'ansible-roles.sublimetext3_packagecontrol', - }), + static openApi = { + '/ansible/role/{roleId}': { + get: { + summary: 'Ansible Galaxy Role Name', + parameters: pathParams({ name: 'roleId', example: '3078' }), + }, }, - ] + } static defaultBadgeData = { label: 'role' } diff --git a/services/appveyor/appveyor-build.service.js b/services/appveyor/appveyor-build.service.js index 29cb395835394..cc71150448669 100644 --- a/services/appveyor/appveyor-build.service.js +++ b/services/appveyor/appveyor-build.service.js @@ -1,23 +1,31 @@ import { renderBuildStatusBadge } from '../build-status.js' +import { pathParams } from '../index.js' import AppVeyorBase from './appveyor-base.js' export default class AppVeyorBuild extends AppVeyorBase { static route = this.buildRoute('appveyor/build') - static examples = [ - { - title: 'AppVeyor', - pattern: ':user/:repo', - namedParams: { user: 'gruntjs', repo: 'grunt' }, - staticPreview: this.render({ status: 'success' }), + static openApi = { + '/appveyor/build/{user}/{repo}': { + get: { + summary: 'AppVeyor Build', + parameters: pathParams( + { name: 'user', example: 'gruntjs' }, + { name: 'repo', example: 'grunt' }, + ), + }, }, - { - title: 'AppVeyor branch', - pattern: ':user/:repo/:branch', - namedParams: { user: 'gruntjs', repo: 'grunt', branch: 'master' }, - staticPreview: this.render({ status: 'success' }), + '/appveyor/build/{user}/{repo}/{branch}': { + get: { + summary: 'AppVeyor Build (with branch)', + parameters: pathParams( + { name: 'user', example: 'gruntjs' }, + { name: 'repo', example: 'grunt' }, + { name: 'branch', example: 'master' }, + ), + }, }, - ] + } static render({ status }) { return renderBuildStatusBadge({ status }) diff --git a/services/appveyor/appveyor-job-build.service.js b/services/appveyor/appveyor-job-build.service.js index fc856aa7f25a2..59f5278ec8c38 100644 --- a/services/appveyor/appveyor-job-build.service.js +++ b/services/appveyor/appveyor-job-build.service.js @@ -1,5 +1,5 @@ import { renderBuildStatusBadge } from '../build-status.js' -import { NotFound } from '../index.js' +import { NotFound, pathParams } from '../index.js' import AppVeyorBase from './appveyor-base.js' export default class AppVeyorJobBuild extends AppVeyorBase { @@ -8,29 +8,29 @@ export default class AppVeyorJobBuild extends AppVeyorBase { pattern: ':user/:repo/:job/:branch*', } - static examples = [ - { - title: 'AppVeyor Job', - pattern: ':user/:repo/:job', - namedParams: { - user: 'wpmgprostotema', - repo: 'voicetranscoder', - job: 'Linux', + static openApi = { + '/appveyor/job/build/{user}/{repo}/{job}': { + get: { + summary: 'AppVeyor Job', + parameters: pathParams( + { name: 'user', example: 'wpmgprostotema' }, + { name: 'repo', example: 'voicetranscoder' }, + { name: 'job', example: 'Linux' }, + ), }, - staticPreview: renderBuildStatusBadge({ status: 'success' }), }, - { - title: 'AppVeyor Job branch', - pattern: ':user/:repo/:job/:branch', - namedParams: { - user: 'wpmgprostotema', - repo: 'voicetranscoder', - job: 'Windows', - branch: 'master', + '/appveyor/job/build/{user}/{repo}/{job}/{branch}': { + get: { + summary: 'AppVeyor Job (with branch)', + parameters: pathParams( + { name: 'user', example: 'wpmgprostotema' }, + { name: 'repo', example: 'voicetranscoder' }, + { name: 'job', example: 'Windows' }, + { name: 'branch', example: 'master' }, + ), }, - staticPreview: renderBuildStatusBadge({ status: 'success' }), }, - ] + } transform({ data, jobName }) { if (!('build' in data)) { diff --git a/services/appveyor/appveyor-tests.service.js b/services/appveyor/appveyor-tests.service.js index 05df133dfc843..2bef4b4cc0798 100644 --- a/services/appveyor/appveyor-tests.service.js +++ b/services/appveyor/appveyor-tests.service.js @@ -1,18 +1,12 @@ import { testResultQueryParamSchema, + testResultOpenApiQueryParams, renderTestResultBadge, - documentation, + documentation as description, } from '../test-results.js' +import { pathParams } from '../index.js' import AppVeyorBase from './appveyor-base.js' -const commonPreviewProps = { - passed: 477, - failed: 2, - skipped: 0, - total: 479, - isCompact: false, -} - export default class AppVeyorTests extends AppVeyorBase { static category = 'test-results' static route = { @@ -20,63 +14,35 @@ export default class AppVeyorTests extends AppVeyorBase { queryParamSchema: testResultQueryParamSchema, } - static examples = [ - { - title: 'AppVeyor tests', - pattern: ':user/:repo', - namedParams: { - user: 'NZSmartie', - repo: 'coap-net-iu0to', - }, - staticPreview: this.render(commonPreviewProps), - documentation, - }, - { - title: 'AppVeyor tests (branch)', - pattern: ':user/:repo/:branch', - namedParams: { - user: 'NZSmartie', - repo: 'coap-net-iu0to', - branch: 'master', + static openApi = { + '/appveyor/tests/{user}/{repo}': { + get: { + summary: 'AppVeyor tests', + description, + parameters: [ + ...pathParams( + { name: 'user', example: 'NZSmartie' }, + { name: 'repo', example: 'coap-net-iu0to' }, + ), + ...testResultOpenApiQueryParams, + ], }, - staticPreview: this.render(commonPreviewProps), - documentation, }, - { - title: 'AppVeyor tests (compact)', - pattern: ':user/:repo', - namedParams: { - user: 'NZSmartie', - repo: 'coap-net-iu0to', - }, - queryParams: { compact_message: null }, - staticPreview: this.render({ - ...commonPreviewProps, - isCompact: true, - }), - documentation, - }, - { - title: 'AppVeyor tests with custom labels', - pattern: ':user/:repo', - namedParams: { - user: 'NZSmartie', - repo: 'coap-net-iu0to', - }, - queryParams: { - passed_label: 'good', - failed_label: 'bad', - skipped_label: 'n/a', + '/appveyor/tests/{user}/{repo}/{branch}': { + get: { + summary: 'AppVeyor tests (with branch)', + description, + parameters: [ + ...pathParams( + { name: 'user', example: 'NZSmartie' }, + { name: 'repo', example: 'coap-net-iu0to' }, + { name: 'branch', example: 'master' }, + ), + ...testResultOpenApiQueryParams, + ], }, - staticPreview: this.render({ - ...commonPreviewProps, - passedLabel: 'good', - failedLabel: 'bad', - skippedLabel: 'n/a', - }), - documentation, }, - ] + } static defaultBadgeData = { label: 'tests', diff --git a/services/archlinux/archlinux.service.js b/services/archlinux/archlinux.service.js index 8694c3b38948a..84a6c67fca020 100644 --- a/services/archlinux/archlinux.service.js +++ b/services/archlinux/archlinux.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { renderVersionBadge } from '../version.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const schema = Joi.object({ pkgver: Joi.string().required(), @@ -13,17 +13,27 @@ export default class ArchLinux extends BaseJsonService { pattern: ':repository/:architecture/:packageName', } - static examples = [ - { - title: 'Arch Linux package', - namedParams: { - architecture: 'x86_64', - repository: 'core', - packageName: 'pacman', + static openApi = { + '/archlinux/v/{repository}/{architecture}/{packageName}': { + get: { + summary: 'Arch Linux package', + parameters: pathParams( + { + name: 'repository', + example: 'core', + }, + { + name: 'architecture', + example: 'x86_64', + }, + { + name: 'packageName', + example: 'pacman', + }, + ), }, - staticPreview: renderVersionBadge({ version: '5.1.3' }), }, - ] + } static defaultBadgeData = { label: 'arch linux' } diff --git a/services/bit/bit-components.service.js b/services/bit/bit-components.service.js index 15f27730d4dfc..7b40a08e4ede7 100644 --- a/services/bit/bit-components.service.js +++ b/services/bit/bit-components.service.js @@ -2,7 +2,7 @@ import Joi from 'joi' import { metric } from '../text-formatters.js' import { nonNegativeInteger } from '../validators.js' import { downloadCount } from '../color-formatters.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const collectionSchema = Joi.object({ payload: Joi.object({ @@ -17,14 +17,23 @@ export default class BitComponents extends BaseJsonService { pattern: ':owner/:collection', } - static examples = [ - { - title: 'bit', - namedParams: { owner: 'ramda', collection: 'ramda' }, - staticPreview: this.render({ count: 330 }), - keywords: ['components'], + static openApi = { + '/bit/collection/total-components/{owner}/{collection}': { + get: { + summary: 'Bit', + parameters: pathParams( + { + name: 'owner', + example: 'ramda', + }, + { + name: 'collection', + example: 'ramda', + }, + ), + }, }, - ] + } static defaultBadgeData = { label: 'components' } diff --git a/services/bountysource/bountysource.service.js b/services/bountysource/bountysource.service.js index 8e7ed9963a7e5..36aaa46d230da 100644 --- a/services/bountysource/bountysource.service.js +++ b/services/bountysource/bountysource.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { metric } from '../text-formatters.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const schema = Joi.object({ activity_total: Joi.number().required() }) @@ -8,13 +8,17 @@ export default class Bountysource extends BaseJsonService { static category = 'funding' static route = { base: 'bountysource/team', pattern: ':team/activity' } - static examples = [ - { - title: 'Bountysource', - namedParams: { team: 'mozilla-core' }, - staticPreview: this.render({ total: 53000 }), + static openApi = { + '/bountysource/team/{team}/activity': { + get: { + summary: 'Bountysource', + parameters: pathParams({ + name: 'team', + example: 'mozilla-core', + }), + }, }, - ] + } static defaultBadgeData = { label: 'bounties' } diff --git a/services/cdnjs/cdnjs.service.js b/services/cdnjs/cdnjs.service.js index d1d9e967f59b2..fff7ec39fa5d8 100644 --- a/services/cdnjs/cdnjs.service.js +++ b/services/cdnjs/cdnjs.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { renderVersionBadge } from '../version.js' -import { BaseJsonService, NotFound } from '../index.js' +import { BaseJsonService, NotFound, pathParams } from '../index.js' const cdnjsSchema = Joi.object({ // optional due to non-standard 'not found' condition @@ -11,12 +11,17 @@ export default class Cdnjs extends BaseJsonService { static category = 'version' static route = { base: 'cdnjs/v', pattern: ':library' } - static examples = [ - { - namedParams: { library: 'jquery' }, - staticPreview: this.render({ version: '1.5.2' }), + static openApi = { + '/cdnjs/v/{library}': { + get: { + summary: 'Cdnjs', + parameters: pathParams({ + name: 'library', + example: 'jquery', + }), + }, }, - ] + } static defaultBadgeData = { label: 'cdnjs' } diff --git a/services/chrome-web-store/chrome-web-store-price.service.js b/services/chrome-web-store/chrome-web-store-price.service.js index 99068389d04b6..dfc77db672d7c 100644 --- a/services/chrome-web-store/chrome-web-store-price.service.js +++ b/services/chrome-web-store/chrome-web-store-price.service.js @@ -1,18 +1,22 @@ import { currencyFromCode } from '../text-formatters.js' -import { NotFound } from '../index.js' +import { NotFound, pathParams } from '../index.js' import BaseChromeWebStoreService from './chrome-web-store-base.js' export default class ChromeWebStorePrice extends BaseChromeWebStoreService { static category = 'funding' static route = { base: 'chrome-web-store/price', pattern: ':storeId' } - static examples = [ - { - title: 'Chrome Web Store', - namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' }, - staticPreview: this.render({ priceCurrency: 'USD', price: 0 }), + static openApi = { + '/chrome-web-store/price/{storeId}': { + get: { + summary: 'Chrome Web Store Price', + parameters: pathParams({ + name: 'storeId', + example: 'ogffaloegjglncjfehdfplabnoondfjo', + }), + }, }, - ] + } static defaultBadgeData = { label: 'price' } diff --git a/services/chrome-web-store/chrome-web-store-users.service.js b/services/chrome-web-store/chrome-web-store-users.service.js index 42b6cfa760224..78175213f38cc 100644 --- a/services/chrome-web-store/chrome-web-store-users.service.js +++ b/services/chrome-web-store/chrome-web-store-users.service.js @@ -1,18 +1,22 @@ import { renderDownloadsBadge } from '../downloads.js' -import { redirector, NotFound } from '../index.js' +import { redirector, NotFound, pathParams } from '../index.js' import BaseChromeWebStoreService from './chrome-web-store-base.js' class ChromeWebStoreUsers extends BaseChromeWebStoreService { static category = 'downloads' static route = { base: 'chrome-web-store/users', pattern: ':storeId' } - static examples = [ - { - title: 'Chrome Web Store', - namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' }, - staticPreview: renderDownloadsBadge({ downloads: 573 }), + static openApi = { + '/chrome-web-store/users/{storeId}': { + get: { + summary: 'Chrome Web Store Users', + parameters: pathParams({ + name: 'storeId', + example: 'ogffaloegjglncjfehdfplabnoondfjo', + }), + }, }, - ] + } static defaultBadgeData = { label: 'users' } diff --git a/services/chrome-web-store/chrome-web-store-version.service.js b/services/chrome-web-store/chrome-web-store-version.service.js index e9e42af9596a0..4fac27f85b587 100644 --- a/services/chrome-web-store/chrome-web-store-version.service.js +++ b/services/chrome-web-store/chrome-web-store-version.service.js @@ -1,18 +1,22 @@ import { renderVersionBadge } from '../version.js' -import { NotFound } from '../index.js' +import { NotFound, pathParams } from '../index.js' import BaseChromeWebStoreService from './chrome-web-store-base.js' export default class ChromeWebStoreVersion extends BaseChromeWebStoreService { static category = 'version' static route = { base: 'chrome-web-store/v', pattern: ':storeId' } - static examples = [ - { - title: 'Chrome Web Store', - namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' }, - staticPreview: renderVersionBadge({ version: 'v1.1.0' }), + static openApi = { + '/chrome-web-store/v/{storeId}': { + get: { + summary: 'Chrome Web Store Version', + parameters: pathParams({ + name: 'storeId', + example: 'ogffaloegjglncjfehdfplabnoondfjo', + }), + }, }, - ] + } static defaultBadgeData = { label: 'chrome web store' } diff --git a/services/clearlydefined/clearlydefined-score.service.js b/services/clearlydefined/clearlydefined-score.service.js index d100df5bcca76..85a72bfcfefec 100644 --- a/services/clearlydefined/clearlydefined-score.service.js +++ b/services/clearlydefined/clearlydefined-score.service.js @@ -4,7 +4,7 @@ import { optionalNonNegativeInteger, } from '../validators.js' import { floorCount as floorCountColor } from '../color-formatters.js' -import { BaseJsonService, NotFound } from '../index.js' +import { BaseJsonService, NotFound, pathParams } from '../index.js' const schema = Joi.object({ scores: Joi.object({ @@ -24,19 +24,35 @@ export default class ClearlyDefinedService extends BaseJsonService { pattern: 'score/:type/:provider/:namespace/:name/:revision', } - static examples = [ - { - title: 'ClearlyDefined Score', - namedParams: { - type: 'npm', - provider: 'npmjs', - namespace: '-', - name: 'jquery', - revision: '3.4.1', + static openApi = { + '/clearlydefined/score/{type}/{provider}/{namespace}/{name}/{revision}': { + get: { + summary: 'ClearlyDefined Score', + parameters: pathParams( + { + name: 'type', + example: 'npm', + }, + { + name: 'provider', + example: 'npmjs', + }, + { + name: 'namespace', + example: '-', + }, + { + name: 'name', + example: 'jquery', + }, + { + name: 'revision', + example: '3.4.1', + }, + ), }, - staticPreview: this.render({ score: 88 }), }, - ] + } static defaultBadgeData = { label: 'score' } diff --git a/services/clojars/clojars-downloads.service.js b/services/clojars/clojars-downloads.service.js index 4999095dc4e2e..2c5c693ccbe37 100644 --- a/services/clojars/clojars-downloads.service.js +++ b/services/clojars/clojars-downloads.service.js @@ -1,3 +1,4 @@ +import { pathParams } from '../index.js' import { renderDownloadsBadge } from '../downloads.js' import { BaseClojarsService } from './clojars-base.js' @@ -5,12 +6,17 @@ export default class ClojarsDownloads extends BaseClojarsService { static category = 'downloads' static route = { base: 'clojars/dt', pattern: ':clojar+' } - static examples = [ - { - namedParams: { clojar: 'prismic' }, - staticPreview: renderDownloadsBadge({ downloads: 117 }), + static openApi = { + '/clojars/dt/{clojar}': { + get: { + summary: 'Clojars Downloads', + parameters: pathParams({ + name: 'clojar', + example: 'prismic', + }), + }, }, - ] + } static defaultBadgeData = { label: 'downloads' } diff --git a/services/cocoapods/cocoapods-docs.service.js b/services/cocoapods/cocoapods-docs.service.js index a25403ad65efb..20bdd8b57c304 100644 --- a/services/cocoapods/cocoapods-docs.service.js +++ b/services/cocoapods/cocoapods-docs.service.js @@ -1,6 +1,6 @@ import Joi from 'joi' import { coveragePercentage as coveragePercentageColor } from '../color-formatters.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, pathParams } from '../index.js' const schema = Joi.object({ cocoadocs: Joi.object({ @@ -12,13 +12,17 @@ export default class CocoapodsDocs extends BaseJsonService { static category = 'analysis' static route = { base: 'cocoapods/metrics/doc-percent', pattern: ':spec' } - static examples = [ - { - title: 'Cocoapods doc percentage', - namedParams: { spec: 'AFNetworking' }, - staticPreview: this.render({ percentage: 94 }), + static openApi = { + '/cocoapods/metrics/doc-percent/{spec}': { + get: { + summary: 'Cocoapods doc percentage', + parameters: pathParams({ + name: 'spec', + example: 'AFNetworking', + }), + }, }, - ] + } static defaultBadgeData = { label: 'docs' } diff --git a/services/cocoapods/cocoapods-license.service.js b/services/cocoapods/cocoapods-license.service.js index 95f401b054c8f..d9d84374bd852 100644 --- a/services/cocoapods/cocoapods-license.service.js +++ b/services/cocoapods/cocoapods-license.service.js @@ -1,16 +1,21 @@ +import { pathParams } from '../index.js' import BaseCocoaPodsService from './cocoapods-base.js' export default class CocoapodsLicense extends BaseCocoaPodsService { static category = 'license' static route = { base: 'cocoapods/l', pattern: ':spec' } - static examples = [ - { - title: 'Cocoapods', - namedParams: { spec: 'AFNetworking' }, - staticPreview: this.render({ license: 'MIT' }), + static openApi = { + '/cocoapods/l/{spec}': { + get: { + summary: 'Cocoapods License', + parameters: pathParams({ + name: 'spec', + example: 'AFNetworking', + }), + }, }, - ] + } static defaultBadgeData = { label: 'license' } diff --git a/services/cocoapods/cocoapods-platform.service.js b/services/cocoapods/cocoapods-platform.service.js index 83cb468d84191..c415aadbf4227 100644 --- a/services/cocoapods/cocoapods-platform.service.js +++ b/services/cocoapods/cocoapods-platform.service.js @@ -1,18 +1,21 @@ +import { pathParams } from '../index.js' import BaseCocoaPodsService from './cocoapods-base.js' export default class CocoapodsPlatform extends BaseCocoaPodsService { static category = 'platform-support' static route = { base: 'cocoapods/p', pattern: ':spec' } - static examples = [ - { - title: 'Cocoapods platforms', - namedParams: { spec: 'AFNetworking' }, - staticPreview: this.render({ - platforms: ['ios', 'osx', 'watchos', 'tvos'], - }), + static openApi = { + '/cocoapods/p/{spec}': { + get: { + summary: 'Cocoapods platforms', + parameters: pathParams({ + name: 'spec', + example: 'AFNetworking', + }), + }, }, - ] + } static defaultBadgeData = { label: 'platform' } diff --git a/services/cocoapods/cocoapods-version.service.js b/services/cocoapods/cocoapods-version.service.js index bc55f8306361d..b5d79226b6e4a 100644 --- a/services/cocoapods/cocoapods-version.service.js +++ b/services/cocoapods/cocoapods-version.service.js @@ -1,3 +1,4 @@ +import { pathParams } from '../index.js' import { renderVersionBadge } from '../version.js' import BaseCocoaPodsService from './cocoapods-base.js' @@ -5,13 +6,17 @@ export default class CocoapodsVersion extends BaseCocoaPodsService { static category = 'version' static route = { base: 'cocoapods/v', pattern: ':spec' } - static examples = [ - { - title: 'Cocoapods', - namedParams: { spec: 'AFNetworking' }, - staticPreview: renderVersionBadge({ version: 'v3.2.1' }), + static openApi = { + '/cocoapods/v/{spec}': { + get: { + summary: 'Cocoapods Version', + parameters: pathParams({ + name: 'spec', + example: 'AFNetworking', + }), + }, }, - ] + } static defaultBadgeData = { label: 'pod' } diff --git a/services/coincap/coincap-changepercent24hr.service.js b/services/coincap/coincap-changepercent24hr.service.js index 29b853b8d499e..2906cc7634943 100644 --- a/services/coincap/coincap-changepercent24hr.service.js +++ b/services/coincap/coincap-changepercent24hr.service.js @@ -1,4 +1,5 @@ import Joi from 'joi' +import { pathParams } from '../index.js' import { floorCount } from '../color-formatters.js' import BaseCoincapService from './coincap-base.js' @@ -14,16 +15,17 @@ const schema = Joi.object({ export default class CoincapChangePercent24HrUsd extends BaseCoincapService { static route = { base: 'coincap/change-percent-24hr', pattern: ':assetId' } - static examples = [ - { - title: 'Coincap (Change Percent 24Hr)', - namedParams: { assetId: 'bitcoin' }, - staticPreview: this.render({ - asset: { name: 'bitcoin', changePercent24Hr: '2.0670573674501840"' }, - }), - keywords: ['bitcoin', 'crypto', 'cryptocurrency'], + static openApi = { + '/coincap/change-percent-24hr/{assetId}': { + get: { + summary: 'Coincap (Change Percent 24Hr)', + parameters: pathParams({ + name: 'assetId', + example: 'bitcoin', + }), + }, }, - ] + } static percentFormat(changePercent24Hr) { return `${parseInt(changePercent24Hr).toFixed(2)}%` diff --git a/services/coincap/coincap-priceusd.service.js b/services/coincap/coincap-priceusd.service.js index 94c1e477c435f..f594c68415858 100644 --- a/services/coincap/coincap-priceusd.service.js +++ b/services/coincap/coincap-priceusd.service.js @@ -1,4 +1,5 @@ import Joi from 'joi' +import { pathParams } from '../index.js' import BaseCoincapService from './coincap-base.js' const schema = Joi.object({ @@ -13,16 +14,17 @@ const schema = Joi.object({ export default class CoincapPriceUsd extends BaseCoincapService { static route = { base: 'coincap/price-usd', pattern: ':assetId' } - static examples = [ - { - title: 'Coincap (Price USD)', - namedParams: { assetId: 'bitcoin' }, - staticPreview: this.render({ - asset: { name: 'bitcoin', priceUsd: '19116.0479117336250772' }, - }), - keywords: ['bitcoin', 'crypto', 'cryptocurrency'], + static openApi = { + '/coincap/price-usd/{assetId}': { + get: { + summary: 'Coincap (Price USD)', + parameters: pathParams({ + name: 'assetId', + example: 'bitcoin', + }), + }, }, - ] + } static priceFormat(price) { return `$${parseFloat(price) diff --git a/services/coincap/coincap-rank.service.js b/services/coincap/coincap-rank.service.js index 7240b4ee9286a..7566b3314e8f1 100644 --- a/services/coincap/coincap-rank.service.js +++ b/services/coincap/coincap-rank.service.js @@ -1,4 +1,5 @@ import Joi from 'joi' +import { pathParams } from '../index.js' import BaseCoincapService from './coincap-base.js' const schema = Joi.object({ @@ -13,14 +14,17 @@ const schema = Joi.object({ export default class CoincapRank extends BaseCoincapService { static route = { base: 'coincap/rank', pattern: ':assetId' } - static examples = [ - { - title: 'Coincap (Rank)', - namedParams: { assetId: 'bitcoin' }, - staticPreview: this.render({ asset: { name: 'bitcoin', rank: '1' } }), - keywords: ['bitcoin', 'crypto', 'cryptocurrency'], + static openApi = { + '/coincap/rank/{assetId}': { + get: { + summary: 'Coincap (Rank)', + parameters: pathParams({ + name: 'assetId', + example: 'bitcoin', + }), + }, }, - ] + } static render({ asset }) { return { diff --git a/services/docsrs/docsrs.service.js b/services/docsrs/docsrs.service.js index f1c9d5f4dda73..8ca40e0bc61cc 100644 --- a/services/docsrs/docsrs.service.js +++ b/services/docsrs/docsrs.service.js @@ -1,14 +1,9 @@ import Joi from 'joi' import { BaseJsonService } from '../index.js' -const schema = Joi.array() - .items( - Joi.object({ - build_status: Joi.boolean().required(), - }), - ) - .min(1) - .required() +const schema = Joi.object({ + doc_status: Joi.boolean().required(), +}).required() export default class DocsRs extends BaseJsonService { static category = 'build' @@ -17,19 +12,19 @@ export default class DocsRs extends BaseJsonService { { title: 'docs.rs', namedParams: { crate: 'regex', version: 'latest' }, - staticPreview: this.render({ version: 'latest', buildStatus: true }), + staticPreview: this.render({ version: 'latest', docStatus: true }), keywords: ['rust'], }, ] static defaultBadgeData = { label: 'docs' } - static render({ buildStatus, version }) { + static render({ docStatus, version }) { let label = `docs@${version}` if (version === 'latest') { label = 'docs' } - if (buildStatus) { + if (docStatus) { return { label, message: 'passing', @@ -47,12 +42,12 @@ export default class DocsRs extends BaseJsonService { async fetch({ crate, version }) { return await this._requestJson({ schema, - url: `https://docs.rs/crate/${crate}/${version}/builds.json`, + url: `https://docs.rs/crate/${crate}/${version}/status.json`, }) } async handle({ crate, version = 'latest' }) { - const [{ build_status: buildStatus }] = await this.fetch({ crate, version }) - return this.constructor.render({ version, buildStatus }) + const { doc_status: docStatus } = await this.fetch({ crate, version }) + return this.constructor.render({ version, docStatus }) } } diff --git a/services/docsrs/docsrs.tester.js b/services/docsrs/docsrs.tester.js index eb62b35717751..125ded9191d25 100644 --- a/services/docsrs/docsrs.tester.js +++ b/services/docsrs/docsrs.tester.js @@ -20,3 +20,11 @@ t.create('Getting latest version works') label: 'docs', message: Joi.allow('passing', 'failing'), }) + +t.create('Crate not found') + .get('/not-a-crate/latest.json') + .expectBadge({ label: 'docs', message: 'not found' }) + +t.create('Version not found') + .get('/tokio/not-a-version.json') + .expectBadge({ label: 'docs', message: 'not found' }) diff --git a/services/dynamic/dynamic-json.service.js b/services/dynamic/dynamic-json.service.js index 68ffd4f6dc54d..f730395073471 100644 --- a/services/dynamic/dynamic-json.service.js +++ b/services/dynamic/dynamic-json.service.js @@ -1,5 +1,5 @@ import { MetricNames } from '../../core/base-service/metric-helper.js' -import { BaseJsonService } from '../index.js' +import { BaseJsonService, queryParams } from '../index.js' import { createRoute } from './dynamic-helpers.js' import jsonPath from './json-path.js' @@ -14,13 +14,11 @@ export default class DynamicJson extends jsonPath(BaseJsonService) { The Dynamic JSON Badge allows you to extract an arbitrary value from any JSON Document using a JSONPath selector and show it on a badge.

`, - parameters: [ + parameters: queryParams( { name: 'url', description: 'The URL to a JSON document', - in: 'query', required: true, - schema: { type: 'string' }, example: 'https://github.com/badges/shields/raw/master/package.json', }, @@ -28,28 +26,20 @@ export default class DynamicJson extends jsonPath(BaseJsonService) { name: 'query', description: 'A JSONPath expression that will be used to query the document', - in: 'query', required: true, - schema: { type: 'string' }, example: '$.name', }, { name: 'prefix', description: 'Optional prefix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: '[', }, { name: 'suffix', description: 'Optional suffix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: ']', }, - ], + ), }, }, } diff --git a/services/dynamic/dynamic-xml.service.js b/services/dynamic/dynamic-xml.service.js index fd4216c5a0302..cd84d2f547868 100644 --- a/services/dynamic/dynamic-xml.service.js +++ b/services/dynamic/dynamic-xml.service.js @@ -2,7 +2,12 @@ import { DOMParser } from '@xmldom/xmldom' import xpath from 'xpath' import { MetricNames } from '../../core/base-service/metric-helper.js' import { renderDynamicBadge, httpErrors } from '../dynamic-common.js' -import { BaseService, InvalidResponse, InvalidParameter } from '../index.js' +import { + BaseService, + InvalidResponse, + InvalidParameter, + queryParams, +} from '../index.js' import { createRoute } from './dynamic-helpers.js' // This service extends BaseService because it uses a different XML parser @@ -23,41 +28,31 @@ export default class DynamicXml extends BaseService { The Dynamic XML Badge allows you to extract an arbitrary value from any XML Document using an XPath selector and show it on a badge.

`, - parameters: [ + parameters: queryParams( { name: 'url', description: 'The URL to a XML document', - in: 'query', required: true, - schema: { type: 'string' }, example: 'https://httpbin.org/xml', }, { name: 'query', description: 'A XPath expression that will be used to query the document', - in: 'query', required: true, - schema: { type: 'string' }, example: '//slideshow/slide[1]/title', }, { name: 'prefix', description: 'Optional prefix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: '[', }, { name: 'suffix', description: 'Optional suffix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: ']', }, - ], + ), }, }, } diff --git a/services/dynamic/dynamic-yaml.service.js b/services/dynamic/dynamic-yaml.service.js index c1a5ab29ce229..0857c8ef144b4 100644 --- a/services/dynamic/dynamic-yaml.service.js +++ b/services/dynamic/dynamic-yaml.service.js @@ -1,5 +1,5 @@ import { MetricNames } from '../../core/base-service/metric-helper.js' -import { BaseYamlService } from '../index.js' +import { BaseYamlService, queryParams } from '../index.js' import { createRoute } from './dynamic-helpers.js' import jsonPath from './json-path.js' @@ -14,13 +14,11 @@ export default class DynamicYaml extends jsonPath(BaseYamlService) { The Dynamic YAML Badge allows you to extract an arbitrary value from any YAML Document using a JSONPath selector and show it on a badge.

`, - parameters: [ + parameters: queryParams( { name: 'url', description: 'The URL to a YAML document', - in: 'query', required: true, - schema: { type: 'string' }, example: 'https://raw.githubusercontent.com/badges/shields/master/.github/dependabot.yml', }, @@ -28,28 +26,20 @@ export default class DynamicYaml extends jsonPath(BaseYamlService) { name: 'query', description: 'A JSONPath expression that will be used to query the document', - in: 'query', required: true, - schema: { type: 'string' }, example: '$.version', }, { name: 'prefix', description: 'Optional prefix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: '[', }, { name: 'suffix', description: 'Optional suffix to append to the value', - in: 'query', - required: false, - schema: { type: 'string' }, example: ']', }, - ], + ), }, }, } diff --git a/services/endpoint/endpoint.service.js b/services/endpoint/endpoint.service.js index 46bc6c8412615..33d4788b02804 100644 --- a/services/endpoint/endpoint.service.js +++ b/services/endpoint/endpoint.service.js @@ -3,7 +3,7 @@ import Joi from 'joi' import { httpErrors } from '../dynamic-common.js' import { optionalUrl } from '../validators.js' import { fetchEndpointData } from '../endpoint-common.js' -import { BaseJsonService, InvalidParameter } from '../index.js' +import { BaseJsonService, InvalidParameter, queryParams } from '../index.js' const blockedDomains = ['github.com', 'shields.io'] @@ -135,16 +135,12 @@ export default class Endpoint extends BaseJsonService { get: { summary: 'Endpoint Badge', description, - parameters: [ - { - name: 'url', - description: 'The URL to your JSON endpoint', - in: 'query', - required: true, - schema: { type: 'string' }, - example: 'https://shields.redsparr0w.com/2473/monday', - }, - ], + parameters: queryParams({ + name: 'url', + description: 'The URL to your JSON endpoint', + required: true, + example: 'https://shields.redsparr0w.com/2473/monday', + }), }, }, } diff --git a/services/github/github-api-provider.integration.js b/services/github/github-api-provider.integration.js index 4e5832c90ef06..ce02069d2af72 100644 --- a/services/github/github-api-provider.integration.js +++ b/services/github/github-api-provider.integration.js @@ -17,11 +17,32 @@ describe('Github API provider', function () { let githubApiProvider - context('without token pool', function () { + context('with no auth', function () { before(function () { githubApiProvider = new GithubApiProvider({ baseUrl, - withPooling: false, + authType: GithubApiProvider.AUTH_TYPES.NO_AUTH, + }) + }) + + it('should be able to run 10 requests', async function () { + this.timeout('20s') + for (let i = 0; i < 10; ++i) { + const { res } = await githubApiProvider.fetch( + fetch, + '/repos/rust-lang/rust', + {}, + ) + expect(res.statusCode).to.equal(200) + } + }) + }) + + context('with global token', function () { + before(function () { + githubApiProvider = new GithubApiProvider({ + baseUrl, + authType: GithubApiProvider.AUTH_TYPES.GLOBAL_TOKEN, globalToken: token, reserveFraction, }) @@ -30,7 +51,12 @@ describe('Github API provider', function () { it('should be able to run 10 requests', async function () { this.timeout('20s') for (let i = 0; i < 10; ++i) { - await githubApiProvider.fetch(fetch, '/repos/rust-lang/rust', {}) + const { res } = await githubApiProvider.fetch( + fetch, + '/repos/rust-lang/rust', + {}, + ) + expect(res.statusCode).to.equal(200) } }) }) @@ -40,7 +66,7 @@ describe('Github API provider', function () { before(function () { githubApiProvider = new GithubApiProvider({ baseUrl, - withPooling: true, + authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL, reserveFraction, }) githubApiProvider.addToken(token) diff --git a/services/github/github-api-provider.js b/services/github/github-api-provider.js index a37513761b1a9..4adf5d5ab29d7 100644 --- a/services/github/github-api-provider.js +++ b/services/github/github-api-provider.js @@ -33,11 +33,17 @@ const bodySchema = Joi.object({ // Provides an interface to the Github API. Manages the base URL. class GithubApiProvider { + static AUTH_TYPES = { + NO_AUTH: 'No Auth', + GLOBAL_TOKEN: 'Global Token', + TOKEN_POOL: 'Token Pool', + } + // reserveFraction: The amount of much of a token's quota we avoid using, to // reserve it for the user. constructor({ baseUrl, - withPooling = true, + authType = this.constructor.AUTH_TYPES.NO_AUTH, onTokenInvalidated = tokenString => {}, globalToken, reserveFraction = 0.25, @@ -45,13 +51,13 @@ class GithubApiProvider { }) { Object.assign(this, { baseUrl, - withPooling, + authType, onTokenInvalidated, globalToken, reserveFraction, }) - if (this.withPooling) { + if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) { this.standardTokens = new TokenPool({ batchSize: 25 }) this.searchTokens = new TokenPool({ batchSize: 5 }) this.graphqlTokens = new TokenPool({ batchSize: 25 }) @@ -60,7 +66,7 @@ class GithubApiProvider { } addToken(tokenString) { - if (this.withPooling) { + if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) { this.standardTokens.add(tokenString) this.searchTokens.add(tokenString) this.graphqlTokens.add(tokenString) @@ -157,7 +163,7 @@ class GithubApiProvider { let token let tokenString - if (this.withPooling) { + if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) { try { token = this.tokenForUrl(url) } catch (e) { @@ -167,7 +173,7 @@ class GithubApiProvider { }) } tokenString = token.id - } else { + } else if (this.authType === this.constructor.AUTH_TYPES.GLOBAL_TOKEN) { tokenString = this.globalToken } @@ -176,14 +182,20 @@ class GithubApiProvider { ...{ headers: { 'User-Agent': userAgent, - Authorization: `token ${tokenString}`, 'X-GitHub-Api-Version': this.restApiVersion, ...options.headers, }, }, } + if ( + this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL || + this.authType === this.constructor.AUTH_TYPES.GLOBAL_TOKEN + ) { + mergedOptions.headers.Authorization = `token ${tokenString}` + } + const response = await requestFetcher(`${baseUrl}${url}`, mergedOptions) - if (this.withPooling) { + if (this.authType === this.constructor.AUTH_TYPES.TOKEN_POOL) { if (response.res.statusCode === 401) { this.invalidateToken(token) } else if (response.res.statusCode < 500) { diff --git a/services/github/github-api-provider.spec.js b/services/github/github-api-provider.spec.js index ae9b9fdaff06b..31f4dcb5187fa 100644 --- a/services/github/github-api-provider.spec.js +++ b/services/github/github-api-provider.spec.js @@ -8,7 +8,11 @@ describe('Github API provider', function () { let mockStandardToken, mockSearchToken, mockGraphqlToken, provider beforeEach(function () { - provider = new GithubApiProvider({ baseUrl, reserveFraction }) + provider = new GithubApiProvider({ + baseUrl, + authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL, + reserveFraction, + }) mockStandardToken = { update: sinon.spy(), invalidate: sinon.spy() } sinon.stub(provider.standardTokens, 'next').returns(mockStandardToken) diff --git a/services/github/github-auth-service.js b/services/github/github-auth-service.js index b26bec191cb67..67f249f02c9e5 100644 --- a/services/github/github-auth-service.js +++ b/services/github/github-auth-service.js @@ -63,7 +63,14 @@ class GithubAuthV4Service extends BaseGraphqlService { `, ) - return super._requestGraphql({ ...attrs, ...{ url, query } }) + return super._requestGraphql({ + ...attrs, + ...{ + url, + query, + httpErrorMessages: { 401: 'auth required for graphql api' }, + }, + }) } } diff --git a/services/github/github-auth-service.spec.js b/services/github/github-auth-service.spec.js index 378d5034f6cf6..d75df3f9db6d4 100644 --- a/services/github/github-auth-service.spec.js +++ b/services/github/github-auth-service.spec.js @@ -41,6 +41,7 @@ describe('GithubAuthV3Service', function () { ) const githubApiProvider = new GithubApiProvider({ baseUrl: 'https://github-api.example.com', + authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL, restApiVersion: '2022-11-28', }) const mockToken = { update: sinon.mock(), invalidate: sinon.mock() } diff --git a/services/github/github-constellation.js b/services/github/github-constellation.js index 75754b6c2bcd0..cafdb52d0d18c 100644 --- a/services/github/github-constellation.js +++ b/services/github/github-constellation.js @@ -23,19 +23,28 @@ class GithubConstellation { this._debugEnabled = config.service.debug.enabled this._debugIntervalSeconds = config.service.debug.intervalSeconds + let authType = GithubApiProvider.AUTH_TYPES.NO_AUTH + const { postgres_url: pgUrl, gh_token: globalToken } = config.private if (pgUrl) { - log.log('Token persistence configured with dbUrl') + log.log('Github Token persistence configured with pgUrl') this.persistence = new SqlTokenPersistence({ url: pgUrl, table: 'github_user_tokens', }) + authType = GithubApiProvider.AUTH_TYPES.TOKEN_POOL + } + + if (globalToken) { + authType = GithubApiProvider.AUTH_TYPES.GLOBAL_TOKEN } + log.log(`Github using auth type: ${authType}`) + this.apiProvider = new GithubApiProvider({ baseUrl: config.service.baseUri, globalToken, - withPooling: !globalToken, + authType, onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString), restApiVersion: config.service.restApiVersion, }) @@ -52,7 +61,7 @@ class GithubConstellation { } async initialize(server) { - if (!this.apiProvider.withPooling) { + if (this.apiProvider.authType !== GithubApiProvider.AUTH_TYPES.TOKEN_POOL) { return } diff --git a/services/github/github-downloads.service.js b/services/github/github-downloads.service.js index 346f55298a994..69c28492c7558 100644 --- a/services/github/github-downloads.service.js +++ b/services/github/github-downloads.service.js @@ -247,7 +247,7 @@ export default class GithubDownloads extends GithubAuthV3Service { } if (releases.length === 0) { - throw new NotFound({ prettyMessage: 'no releases' }) + throw new NotFound({ prettyMessage: 'no releases found' }) } const { downloads } = this.constructor.transform({ diff --git a/services/github/github-downloads.tester.js b/services/github/github-downloads.tester.js index dfee08ad7aea5..11f833c947f29 100644 --- a/services/github/github-downloads.tester.js +++ b/services/github/github-downloads.tester.js @@ -19,11 +19,11 @@ t.create('Downloads all releases') t.create('Downloads all releases (no releases)') .get('/downloads/badges/shields/total.json') - .expectBadge({ label: 'downloads', message: 'no releases' }) + .expectBadge({ label: 'downloads', message: 'no releases found' }) t.create('Downloads-pre all releases (no releases)') .get('/downloads-pre/badges/shields/total.json') - .expectBadge({ label: 'downloads', message: 'no releases' }) + .expectBadge({ label: 'downloads', message: 'no releases found' }) t.create('Downloads all releases (repo not found)') .get('/downloads/badges/helmets/total.json') @@ -154,7 +154,7 @@ t.create('downloads-pre for latest release') // https://github.com/badges/shields/issues/3786 t.create('downloads-pre for latest release (no-releases)') .get('/downloads-pre/badges/shields/latest/total.json') - .expectBadge({ label: 'downloads', message: 'no releases' }) + .expectBadge({ label: 'downloads', message: 'no releases found' }) t.create('downloads for release without slash') .get('/downloads/atom/atom/v0.190.0/total.json') diff --git a/services/github/github-lerna-json.tester.js b/services/github/github-lerna-json.tester.js index d4097a99c9673..95694ee27d72a 100644 --- a/services/github/github-lerna-json.tester.js +++ b/services/github/github-lerna-json.tester.js @@ -7,12 +7,10 @@ t.create('Lerna version').get('/facebook/jest.json').expectBadge({ message: isSemver, }) -t.create('Lerna version (independent)') - .get('/jneander/jneander.json') - .expectBadge({ - label: 'lerna', - message: 'independent', - }) +t.create('Lerna version (independent)').get('/imba/imba.json').expectBadge({ + label: 'lerna', + message: 'independent', +}) t.create('Lerna version (branch)').get('/facebook/jest/main.json').expectBadge({ label: 'lerna@main', diff --git a/services/github/github-package-json.tester.js b/services/github/github-package-json.tester.js index 7ba422d534da5..28fa94408f5f4 100644 --- a/services/github/github-package-json.tester.js +++ b/services/github/github-package-json.tester.js @@ -93,9 +93,9 @@ t.create('Prod dependency version (monorepo)') }) t.create('Scoped dependency') - .get('/dependency-version/badges/shields/dev/@babel/core.json') + .get('/dependency-version/badges/shields/dev/@docusaurus/core.json') .expectBadge({ - label: '@babel/core', + label: '@docusaurus/core', message: semverRange, }) diff --git a/services/github/github-pipenv.tester.js b/services/github/github-pipenv.tester.js index 50b74f12b9356..578a85c91b95c 100644 --- a/services/github/github-pipenv.tester.js +++ b/services/github/github-pipenv.tester.js @@ -82,8 +82,10 @@ t.create('Locked version of unknown dependency') }) t.create('Locked version of VCS dependency') - .get('/locked/dependency-version/GSS-Cogs/databaker-docker/databaker.json') + .get( + '/locked/dependency-version/thorn-oss/perception/dev/videoalignment.json', + ) .expectBadge({ - label: 'databaker', + label: 'videoalignment', message: isShortSha, }) diff --git a/services/github/github-search.service.js b/services/github/github-search.service.js index 69ace99dd0309..cd50e6abdb468 100644 --- a/services/github/github-search.service.js +++ b/services/github/github-search.service.js @@ -2,7 +2,7 @@ import Joi from 'joi' import { metric } from '../text-formatters.js' import { nonNegativeInteger } from '../validators.js' import { GithubAuthV3Service } from './github-auth-service.js' -import { httpErrorsFor, documentation } from './github-helpers.js' +import { documentation } from './github-helpers.js' const schema = Joi.object({ total_count: nonNegativeInteger }).required() @@ -49,7 +49,11 @@ export default class GithubSearch extends GithubAuthV3Service { }, }, schema, - httpErrors: httpErrorsFor('repo not found'), + httpErrors: { + 401: 'auth required for search api', + 404: 'repo not found', + 422: 'repo not found', + }, }) return this.constructor.render({ query, totalCount }) } diff --git a/services/github/github-search.tester.js b/services/github/github-search.tester.js index cb3fa80564a1c..a220560a63d6b 100644 --- a/services/github/github-search.tester.js +++ b/services/github/github-search.tester.js @@ -8,4 +8,4 @@ t.create('hit counter') t.create('hit counter for nonexistent repo') .get('/badges/puppets/async%20handle.json') - .expectBadge({ label: 'counter', message: 'repo not found' }) + .expectBadge({ label: 'async handle counter', message: '0' }) diff --git a/services/github/github-workflow-status.service.js b/services/github/github-workflow-status.service.js index 69e6e8f286d8d..9881676db3cf7 100644 --- a/services/github/github-workflow-status.service.js +++ b/services/github/github-workflow-status.service.js @@ -8,7 +8,7 @@ export default class DeprecatedGithubWorkflowStatus extends BaseService { pattern: ':various+', } - static examples = [] + static openApi = {} static defaultBadgeData = { label: 'build' } diff --git a/services/open-vsx/open-vsx-base.js b/services/open-vsx/open-vsx-base.js index b7a79656604bb..3f4d43d5ce785 100644 --- a/services/open-vsx/open-vsx-base.js +++ b/services/open-vsx/open-vsx-base.js @@ -31,8 +31,8 @@ export default class OpenVSXBase extends BaseJsonService { async fetch({ namespace, extension, version }) { return this._requestJson({ schema: extensionQuerySchema, - url: `https://open-vsx.org/api/${namespace}/${extension}/${ - version || '' + url: `https://open-vsx.org/api/${namespace}/${extension}${ + version ? `/${version}` : '' }`, httpErrors: { 400: 'invalid extension id', diff --git a/services/test-results.js b/services/test-results.js index 84f6831762087..93e21c3f36a9a 100644 --- a/services/test-results.js +++ b/services/test-results.js @@ -1,4 +1,5 @@ import Joi from 'joi' +import { queryParams } from './index.js' const testResultQueryParamSchema = Joi.object({ compact_message: Joi.equal(''), @@ -7,6 +8,17 @@ const testResultQueryParamSchema = Joi.object({ skipped_label: Joi.string(), }).required() +const testResultOpenApiQueryParams = queryParams( + { + name: 'compact_message', + example: null, + schema: { type: 'boolean' }, + }, + { name: 'passed_label', example: 'good' }, + { name: 'failed_label', example: 'bad' }, + { name: 'skipped_label', example: 'n/a' }, +) + function renderTestResultMessage({ passed, failed, @@ -89,13 +101,13 @@ const documentation = `

For example, if you want to use a different terminology: -
+
?passed_label=good&failed_label=bad&skipped_label=n%2Fa

Or symbols: -
+
?compact_message&passed_label=💃&failed_label=🤦‍♀️&skipped_label=🤷

@@ -106,6 +118,7 @@ const documentation = ` export { testResultQueryParamSchema, + testResultOpenApiQueryParams, renderTestResultMessage, renderTestResultBadge, documentation,