diff --git a/src/routes/api.ts b/src/routes/api.ts index 606865a..c781650 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -33,7 +33,7 @@ const createHandleGetApi = (registry: OpenAPIRegistry) => { def.route.request.query = def.route.request.query.extend({ output: z.string().optional().openapi({ description: - 'Use the output value human to receive the JSON results in pretty print format, presented on a HTML page.', + 'Request a `human` output to receive the JSON results in pretty print format, presented on a HTML page.', }), }); @@ -151,8 +151,12 @@ export default (app: Hono, registry: OpenAPIRegistry) => { registry.registerPath({ method: 'get', path: '/api', - summary: 'Get OpenAPI Specification', - description: 'Returns the OpenAPI specification for the cdnjs API.', + summary: 'Getting the OpenAPI specification for the cdnjs API', + description: [ + 'The `/api` endpoint will return a JSON object with full OpenAPI specification for the cdnjs API.', + '', + 'The cache lifetime on this endpoint is six hours.', + ].join('\n'), tags: ['meta'], responses: { 200: { diff --git a/src/routes/errors.schema.ts b/src/routes/errors.schema.ts index 717d73a..fbf9a69 100644 --- a/src/routes/errors.schema.ts +++ b/src/routes/errors.schema.ts @@ -7,6 +7,6 @@ export const errorResponseSchema = z message: z.string(), ref: z.string().optional(), }) - .openapi('Error'); + .openapi('Error', { description: 'An error response from the cdnjs API' }); export type ErrorResponse = z.infer; diff --git a/src/routes/libraries.schema.ts b/src/routes/libraries.schema.ts index 2869d42..ac10d52 100644 --- a/src/routes/libraries.schema.ts +++ b/src/routes/libraries.schema.ts @@ -12,7 +12,10 @@ export const librariesResponseSchema = z.object({ latest: z.string().nullable(), }), ) - .openapi('LibraryResult'), + .openapi('LibraryResult', { + description: + 'Information about a library on cdnjs when browsing the libraries list', + }), ), total: z.number(), available: z.number(), diff --git a/src/routes/libraries.ts b/src/routes/libraries.ts index 0445f50..ffad82c 100644 --- a/src/routes/libraries.ts +++ b/src/routes/libraries.ts @@ -72,23 +72,54 @@ export default (app: Hono, registry: OpenAPIRegistry) => { method: 'get', path: '/libraries', summary: 'Browsing all libraries on cdnjs', - description: - 'The `/libraries` endpoint will return a JSON object with three top-level properties.\n\nThis API endpoint can also be used to search cdnjs for libraries, by making use of the optional `search` URL query parameter.\n\nThe cache lifetime on this endpoint is six hours.', + description: [ + 'The `/libraries` endpoint will return a JSON object with key information on all available libraries on cdnjs.', + '', + 'This API endpoint can also be used to search cdnjs for specific libraries, by making use of the optional `search` URL query parameter.', + '', + 'The cache lifetime on this endpoint is six hours.', + ].join('\n'), tags: ['libraries'], request: { query: z.object({ - search: z.string().optional().openapi({ - description: - "The value to use when searching the libraries index on cdnjs.\n\nLibraries will not be ranked by search relevance when they are returned, they will be ranked using the same ranking as when no search query is provided.\n\n*This ranking is done by Algolia and is primarily based on the number of stars each library's associated GitHub repo has.*", - }), - fields: z.string().optional().openapi({ - description: - 'Provide a comma-separated string of fields to return in each library object from the cdnjs Algolia index. name and latest will always be present in every object. Any field requested that does not exist will be included in each object with a null value. Currently, the following fields (case-sensitive) are published in the Algolia index for each library and can be requested via this parameter: filename, description, version, keywords, alternativeNames, fileType, github, objectID, license, homepage, repository, author, originalName, sri. The available fields are based on the SearchEntry structure in our tools repo.', - }), - search_fields: z.string().optional().openapi({ - description: - 'Provide a comma-separated string of fields to be considered when searching for a given search query parameter. Not all fields are supported for this, any unsupported fields given will be silently ignored. Currently, the following fields (case-sensitive) are supported: name, alternativeNames, github.repo, description, keywords, filename, repositories.url, github.user, maintainers.name. The supported fields are controlled by our Algolia settings and are mirrored in the API server libraries route logic.', - }), + search: z + .string() + .optional() + .openapi({ + description: [ + 'The value to use when searching the libraries index on cdnjs.', + '', + 'Libraries will not be ranked by search relevance when they are returned, they will be ranked using the same ranking as when no search query is provided.', + '', + "*This ranking is done by Algolia and is primarily based on the number of stars each library's associated GitHub repo has.*", + ].join('\n'), + }), + fields: z + .string() + .optional() + .openapi({ + description: [ + 'Provide a comma-separated string of fields to return in each library object from the cdnjs Algolia index.', + '', + 'The following fields (case-sensitive) are published in the Algolia index for each library and can be requested via this parameter: `filename`, `description`, `version`, `keywords`, `alternativeNames`, `fileType`, `github`, `objectID`, `license`, `homepage`, `repository`, `author`, `originalName`, `sri`.', + '', + 'The `name` and `latest` fields are always returned for each result. If no field are specified, only these fields will be returned. `*` can be provided to return all available fields.', + '', + '*The available fields are based on the [SearchEntry structure in our tools repo](https://github.com/cdnjs/tools/blob/master/cmd/algolia/main.go).*', + ].join('\n'), + }), + search_fields: z + .string() + .optional() + .openapi({ + description: [ + 'Provide a comma-separated string of fields to be considered when searching for a given search query parameter.', + '', + 'The following fields (case-sensitive) are supported for searching: `name`, `alternativeNames`, `github.repo`, `description`, `keywords`, `filename`, `repositories.url`, `github.user`, `maintainers.name`. Any unsupported fields provided will be silently ignored.', + '', + '*The supported fields are controlled by our Algolia settings and are mirrored in the [API server Algolia libraries logic](https://github.com/cdnjs/api-server/blob/master/src/utils/algolia.ts).*', + ].join('\n'), + }), limit: z.number().optional().openapi({ description: 'Limit the number of library objects that are returned in the results array. This value will be reflected in the total top-level property, but the available property will return the full number with no limit applied.', diff --git a/src/routes/library.schema.ts b/src/routes/library.schema.ts index 64944de..8c0590c 100644 --- a/src/routes/library.schema.ts +++ b/src/routes/library.schema.ts @@ -11,7 +11,10 @@ export const libraryVersionResponseSchema = z sri: z.record(z.string(), z.string()), }) .partial() - .openapi('LibraryVersion'); + .openapi('LibraryVersion', { + description: + 'Information about a specific version of a library on cdnjs', + }); export type LibraryVersionResponse = z.infer< typeof libraryVersionResponseSchema @@ -32,6 +35,8 @@ export const libraryResponseSchema = librarySchema ), }) .partial() - .openapi('Library'); + .openapi('Library', { + description: 'Information about a library on cdnjs', + }); export type LibraryResponse = z.infer; diff --git a/src/routes/library.ts b/src/routes/library.ts index b5857c4..fe831c8 100644 --- a/src/routes/library.ts +++ b/src/routes/library.ts @@ -258,22 +258,27 @@ export default (app: Hono, registry: OpenAPIRegistry) => { method: 'get', path: '/libraries/{library}/{version}', summary: 'Getting a specific version for a library on cdnjs', - description: - 'The `/libraries/:library/:version` endpoint returns a JSON object with details specific to a requested version of a library on cdnjs.\n\nThe cache lifetime on this endpoint is 355 days, identical to the CDN. The response is also marked as immutable, as a version on cdnjs will never change once published.\n\ncdnjs only allows access to specific versions of a library, and these are considered immutable. Access to tags for a library, such as `latest`, is not supported as these have a mutable definition, which would go against what cdnjs aims to provide with long-life caching on responses and SRI hashes.', + description: [ + 'The `/libraries/:library/:version` endpoint returns a JSON object with details specific to a requested version of a library on cdnjs.', + '', + 'The cache lifetime on this endpoint is 355 days, identical to the CDN. The response is also marked as immutable, as a version on cdnjs will never change once published.', + '', + 'cdnjs only allows access to specific versions of a library, and these are considered immutable. Access to tags for a library, such as `latest`, is not supported as these have a mutable definition, which would go against what cdnjs aims to provide with long-life caching on responses and SRI hashes.', + ].join('\n'), tags: ['libraries'], request: { params: z.object({ library: z .string() - .openapi({ description: 'The name of the library.' }), + .openapi({ description: 'Name of the library.' }), version: z .string() - .openapi({ description: 'The version of the library.' }), + .openapi({ description: 'Version of the library.' }), }), query: z.object({ fields: z.string().optional().openapi({ description: - 'Provide a comma-separated string of fields to return.', + 'Provide a comma-separated string of fields to return in the library version object. If no field are specified, all fields will be returned.', }), }), }, @@ -305,19 +310,26 @@ export default (app: Hono, registry: OpenAPIRegistry) => { method: 'get', path: '/libraries/{library}', summary: 'Getting a specific library on cdnjs', - description: - 'Accessing `assets` for all versions of a library using this endpoint is deprecated. The `assets` property now only contains a single entry for the latest version. To access the assets of any version, use the `/libraries/:library/:version` endpoint.\n\nSee [cdnjs/cdnjs issue #14140](https://github.com/cdnjs/cdnjs/issues/14140) for more information.\n\nThe `/libraries/:library` endpoint allows for data on a specific library to be requested and will return a JSON object with all library data properties by default.\n\nThe cache lifetime on this endpoint is six hours.', + description: [ + 'The `/libraries/:library` endpoint allows for data on a specific library to be requested and will return a JSON object with all library data properties by default.', + '', + 'The cache lifetime on this endpoint is six hours.', + '', + '> Accessing `assets` for all versions of a library using this endpoint is deprecated. The `assets` property now only contains a single entry for the latest version. To access the assets of any version, use the `/libraries/:library/:version` endpoint.', + '>', + '> See [cdnjs/cdnjs issue #14140](https://github.com/cdnjs/cdnjs/issues/14140) for more information.', + ].join('\n'), tags: ['libraries'], request: { params: z.object({ library: z .string() - .openapi({ description: 'The name of the library.' }), + .openapi({ description: 'Name of the library.' }), }), query: z.object({ fields: z.string().optional().openapi({ description: - 'Provide a comma-separated string of fields to return in the library object.', + 'Provide a comma-separated string of fields to return in the library object. If no field are specified, all fields will be returned.', }), }), }, diff --git a/src/routes/stats.ts b/src/routes/stats.ts index 60a481b..31e2c07 100644 --- a/src/routes/stats.ts +++ b/src/routes/stats.ts @@ -44,14 +44,17 @@ export default (app: Hono, registry: OpenAPIRegistry) => { method: 'get', path: '/stats', summary: 'Fetch basic statistics for cdnjs', - description: - 'The `/stats` endpoint returns a JSON object containing a set of statistics relating to cdnjs.\n\nThe cache lifetime on this endpoint is 6 hours.', + description: [ + 'The `/stats` endpoint returns a JSON object containing a set of statistics relating to cdnjs.', + '', + 'The cache lifetime on this endpoint is 6 hours.', + ].join('\n'), tags: ['meta'], request: { query: z.object({ fields: z.string().optional().openapi({ description: - 'Provide a comma-separated string of fields to return in the stats object.', + 'Provide a comma-separated string of fields to return in the stats object. If no field are specified, all fields will be returned.', }), }), }, diff --git a/src/routes/whitelist.ts b/src/routes/whitelist.ts index 97cbccc..8eba034 100644 --- a/src/routes/whitelist.ts +++ b/src/routes/whitelist.ts @@ -49,14 +49,17 @@ export default (app: Hono, registry: OpenAPIRegistry) => { method: 'get', path: '/whitelist', summary: 'Fetch details about the cdnjs file extension whitelist', - description: - 'The `/whitelist` endpoint returns a JSON object containing a list of extensions permitted on the CDN as well as categories for those extensions.\n\nThe cache lifetime on this endpoint is 6 hours.', + description: [ + 'The `/whitelist` endpoint returns a JSON object containing a list of extensions permitted on the CDN as well as categories for those extensions.', + '', + 'The cache lifetime on this endpoint is 6 hours.', + ].join('\n'), tags: ['meta'], request: { query: z.object({ fields: z.string().optional().openapi({ description: - 'Provide a comma-separated string of fields to return from the available whitelist data.', + 'Provide a comma-separated string of fields to return from the available whitelist data. If no field are specified, all fields will be returned.', }), }), }, diff --git a/src/utils/jsx/islands/swagger.tsx b/src/utils/jsx/islands/swagger.tsx index 52fdae2..ad71b16 100644 --- a/src/utils/jsx/islands/swagger.tsx +++ b/src/utils/jsx/islands/swagger.tsx @@ -14,6 +14,7 @@ const mixins = { padding: ${theme.spacing(1.5, 2)} !important; border-radius: ${theme.radius}; font-size: ${theme.font.small.size}; + font-weight: ${theme.font.small.weight}; margin: 0; display: block; width: 100%; @@ -27,6 +28,7 @@ const mixins = { padding: ${theme.spacing(0.25, 0.75)}; border-radius: ${theme.radius}; font-size: ${theme.font.small.size}; + font-weight: ${theme.font.small.weight}; `, body: css` padding: ${theme.spacing(1.5, 2)}; @@ -61,6 +63,18 @@ const styles = { } } + blockquote { + background: rgb(from ${theme.background.brand} r g b / 0.05); + border: 1px solid ${theme.background.brand}; + border-radius: ${theme.radius}; + margin: ${theme.spacing(1, 0)}; + padding: ${theme.spacing(0, 2)}; + + p { + margin: ${theme.spacing(1.5, 0)}; + } + } + /* Remove the default styling of operation sections. */ .opblock { margin: 0; @@ -161,6 +175,10 @@ const styles = { /* Make the response schema models match the example value code. */ .model { font-size: ${theme.font.small.size}; + + .description { + display: none; + } } .model-example .model-box { ${mixins.pre}; @@ -493,6 +511,7 @@ const plugin = (system: System) => ({ if (!entry) return null; const [name, schema] = entry; + const { description } = schema.toObject(); const fullPath = [...specPathBase, name]; const expanded = system.layoutSelectors.isShown( fullPath, @@ -515,6 +534,7 @@ const plugin = (system: System) => ({ expanded={expanded} > {name} + {description} {expanded && (