Add KEYS.md for comprehensive OpenAPI key reference and update biome.json to disable explicit any type warnings

This commit is contained in:
Luke Hagar
2025-09-26 15:14:26 +00:00
parent 16b154098f
commit 912ef809bf
6 changed files with 707 additions and 3 deletions

View File

@@ -26,7 +26,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Setup Bun
uses: oven-sh/setup-bun@v1

702
KEYS.md Normal file
View File

@@ -0,0 +1,702 @@
# OpenAPI Key Ordering Reference
This document lists all OpenAPI keys supported by the Prettier OpenAPI plugin, their ordering, and the reasoning from the source code comments.
## Root Level Keys
```typescript
export const RootKeys = [
// Version identifiers
'swagger', // Swagger 2.0
'openapi', // OpenAPI 3.0+
// Schema identifier
'jsonSchemaDialect', // OpenAPI 3.1+
'info',
'externalDocs',
// Common sense grouping for a server definition
'schemes', // Swagger 2.0
'host', // Swagger 2.0
'basePath', // Swagger 2.0
// Typically short arrays, grouped together higher up
'consumes', // Swagger 2.0
'produces', // Swagger 2.0
// Servers is usually really short, and can be helpful to see at the top for quick reference
'servers', // OpenAPI 3.0+ (replaces host, basePath, schemes in 2.0)
// Security is tiny, keep it at the top.
'security',
// Tags are often fairly long, but given that its a fairly core organizational feature, it's helpful to see at the top for quick reference
'tags',
// Paths are usually the longest block, unless components are used heavily, in which case it can be fairly short.
'paths',
// Webhooks are very often a short list, if its included at all, but depending on API structure and usage it can be quite long, having it below paths but above components seems like good placement..
'webhooks', // OpenAPI 3.1+
// Components is usually the longest block when it's heavily used, due to it having sections for reuse in most all other sections.
'components', // OpenAPI 3.0+ (replaces definitions, parameters, responses, securityDefinitions in 2.0)
'definitions', // Swagger 2.0
'parameters', // Swagger 2.0
'responses', // Swagger 2.0
'securityDefinitions', // Swagger 2.0
] as const;
```
## Info Section Keys
```typescript
export const InfoKeys = [
// Title is just a name, usually a single short line.
'title',
// Version is a usually a tiny string, and should be at the top.
'version',
// Summary to me has always been a shorter description, seems appropriate to have it above description.
'summary', // OpenAPI 3.1+
// Description is usually longer if its included alongside a summary.
// I have seen everything from a single line to a veriatable novel.
'description',
// Terms of Service is usually a single line, and should be at the top.
'termsOfService',
// Contact and license are multi-line objects when included, so they should be at the bottom.
'contact',
'license',
] as const;
```
## Contact Keys
```typescript
// This key order should not require explaination.
// If it does let me know and I'll block you.
export const ContactKeys = [
'name',
'email',
'url',
] as const;
```
## License Keys
```typescript
export const LicenseKeys = [
'name',
'identifier',
'url',
] as const;
```
## Components Section Keys
```typescript
// A sane ordering for components.
export const ComponentsKeys = [
// Security is almost alwasy present, and very short, put it at the top.
'securitySchemes',
// I have never actually seen path items included in a specification.
// That being said, I think the general philosophy of larger items at the top,
// with smaller more atomic items used to make up the larger items at the bottom, makes sense.
'pathItems', // OpenAPI 3.1+
// Parameters can be larger, especially in larger APIs with extremely consistent usage patterns, but almost always shorter than schemas.
'parameters',
// Headers are basically just more parameters.
'headers',
// Request bodies are almost never used, I believe this is because the request bodies are usually so different from endpoint to endpoint.
// However, if they are used, Specifying them at this level seems reasonable.
'requestBodies',
// Responses are usually a smaller list, and often only used for global error responses.
'responses',
// Callbacks are essentially another kind of response.
'callbacks',
// Links are programatic ways to link endpoints together.
'links',
// Schemas are frequently the largest block, and are the building blocks that make up most every other section.
'schemas',
// Examples are fairly free form, and logically would be used in schemas, so it make sense to be at the bottom.
'examples',
] as const;
```
## Operation Keys
```typescript
export const OperationKeys = [
// Important short info at a glance.
'summary',
'operationId',
'description',
'externalDocs', // OpenAPI 3.0+
'tags',
'deprecated',
// Security is a often short list, and is usually not included at the operation level.
'security',
// Servers is a often short list, and is usually not included at the operation level.
'servers', // OpenAPI 3.0+
'consumes', // Swagger 2.0
'produces', // Swagger 2.0
// Parameters are ideally added first via $ref, for situations like pagination, and then single endpoint specific parameters inline after.
'parameters',
// Request body is going to be shorter that responses, unless the responses are all `$ref`s
'requestBody', // OpenAPI 3.0+
// Responses come after the request because obviously.
'responses',
// Callbacks are essentially another kind of response.
'callbacks', // OpenAPI 3.0+
// Schemes should never have been included at this level, its just silly, but if they are, put them at the bottom.
'schemes', // Swagger 2.0
] as const;
```
## Parameter Keys
```typescript
export const ParameterKeys = [
// Important short info at a glance.
'name',
'description',
'in',
'required',
'deprecated',
// Semantic formatting options for parameters.
'allowEmptyValue',
'style',
'explode',
'allowReserved',
// Schema is the core of the parameter, and specifies what the parameter actually is.
'schema',
// Content is similar to schema, and is typically only used for more complex parameters.
'content', // OpenAPI 3.0+
// Type and format are the most common schema keys, and should be always be paired together.
'type', // Swagger 2.0
'format', // Swagger 2.0
// When type is array, items should be present.
// collectionFormat is the array equivalent of format.
'items', // Swagger 2.0
'collectionFormat', // Swagger 2.0
// Default is the default value of the parameter when that parameter is not specified.
'default', // Swagger 2.0
// Numeric parameter constraints grouped together
// Min before max, multipleOf in the middle, since its essentially steps between.
'minimum', // Swagger 2.0
'exclusiveMinimum', // Swagger 2.0
'multipleOf', // Swagger 2.0
'maximum', // Swagger 2.0
'exclusiveMaximum', // Swagger 2.0
// String parameter constraints
'pattern', // Swagger 2.0
'minLength', // Swagger 2.0
'maxLength', // Swagger 2.0
// Array parameter constraints
'minItems', // Swagger 2.0
'maxItems', // Swagger 2.0
'uniqueItems', // Swagger 2.0
// Enum is a strict list of allowed values for the parameter.
'enum', // Swagger 2.0
// Example and examples are perfect directly below the schema.
'example',
'examples',
] as const;
```
## Schema Keys
```typescript
export const SchemaKeys = [
// $ref should always be at the top, because when its included there are at most 2 other keys that are present.
'$ref', // JSON Schema draft
// When $id is included it's used as a kind of name, or an id if you will, and should be at the top.
'$id', // JSON Schema draft
// These JSON Schema draft keys are rarely used in my experience.
// They seem to all be extremely short, so are fine to be at the top.
// Anybody who uses them a lot feel free to weigh in here and make an argument for a different placement.
// Schema and Vocabulary appear to be universally be external links, so should be grouped.
'$schema', // JSON Schema draft
'$vocabulary', // JSON Schema draft
// I have no idea on the practical use of these keys, especially in this context,
// but someone using them would likely want them close to the top for reference.
'$anchor', // JSON Schema draft
'$dynamicAnchor', // JSON Schema draft
'$dynamicRef', // JSON Schema draft
'$comment', // JSON Schema draft
'$defs', // JSON Schema draft
'$recursiveAnchor', // JSON Schema draft
'$recursiveRef', // JSON Schema draft
// This is where most non $ref schemas will begin.
// The info section of the schema.
'title',
// description and externalDocs logically come after the title,
// describing it in more and more detail.
'description',
'externalDocs',
// Deprecated is a good at a glance key, and stays at the top.
'deprecated',
// This next section describes the type and how it behaves.
// Type and format should always be grouped together.
'type',
'format',
// Content schema, media type, and encoding are all related to the content of the schema,
// and are similar to format. They should be grouped together.
'contentSchema', // JSON Schema draft
'contentMediaType', // JSON Schema draft
'contentEncoding', // JSON Schema draft
// Nullable is like format, it specifies how the type can behave,
// and in more recent versions of OpenAPI its directly included in the type field.
'nullable',
// Enum and const are both static entries of allowed values for the schema.
// They should be grouped together.
'const',
'enum',
// The default value of the schema when that schema is not specified.
// Same as with parameters, but at the schema level.
'default',
// ReadOnly and WriteOnly are boolean flags and should be grouped together.
'readOnly',
'writeOnly',
// Examples when included should be directly below what they are examples of.
'example',
'examples',
// Numeric constraints grouped together
// Min before max, multipleOf in the middle, since its steps between them.
'minimum',
'exclusiveMinimum',
'multipleOf',
'maximum',
'exclusiveMaximum',
// String constraints grouped together
'pattern',
'minLength',
'maxLength',
// Array constraints grouped together
'uniqueItems',
'minItems',
'maxItems',
'items',
// Prefix items describes tuple like array behavior.
'prefixItems', // JSON Schema draft
// Contains specifies a subschema that must be present in the array.
// Min and max contains specify the match occurrence constraints for the contains key.
'contains', // JSON Schema draft
'minContains', // JSON Schema draft
'maxContains', // JSON Schema draft
// After accounting for Items, prefixItems, and contains, unevaluatedItems specifies if additional items are allowed.
// This key is either a boolean or a subschema.
// Behaves the same as additionalProperties at the object level.
'unevaluatedItems', // JSON Schema draft
// Object constraints grouped together
// min and max properties specify how many properties an object can have.
'minProperties',
'maxProperties',
// Pattern properties are a way to specify a pattern and schemas for properties that match that pattern.
// Additional properties are a way to specify if additional properties are allowed and if so, how they are shaped.
'patternProperties',
'additionalProperties',
// Properties are the actual keys and schemas that make up the object.
'properties',
// Required is a list of those properties that are required to be present in the object.
'required',
// Unevaluated properties specifies if additional properties are allowed after applying all other validation rules.
// This is more powerful than additionalProperties as it considers the effects of allOf, anyOf, oneOf, etc.
'unevaluatedProperties', // JSON Schema draft
// Property names defines a schema that property names must conform to.
// This is useful for validating that all property keys follow a specific pattern or format.
'propertyNames', // JSON Schema draft
// Dependent required specifies properties that become required when certain other properties are present.
// This allows for conditional requirements based on the presence of specific properties.
'dependentRequired', // JSON Schema draft
// Dependent schemas defines schemas that apply when certain properties are present.
// This allows for conditional validation rules based on the presence of specific properties.
// For example, if a property is present, a certain other property must also be present, and match a certain schema.
'dependentSchemas', // JSON Schema draft
// Discriminator is a way to specify a property that differentiates between different types of objects.
// This is useful for polymorphic schemas, and should go above the schema composition keys.
'discriminator',
// Schema composition keys grouped together
// allOf, anyOf, oneOf, and not are all used to compose schemas from other schemas.
// allOf is a logical AND,
// anyOf is a logical OR,
// oneOf is a logical XOR,
// and not is a logical NOT.
'allOf',
'anyOf',
'oneOf',
'not',
// Conditional keys grouped together
'if', // JSON Schema draft
'then', // JSON Schema draft
'else', // JSON Schema draft
// XML is a way to specify the XML serialization of the schema.
// This is useful for APIs that need to support XML serialization.
'xml',
] as const;
```
## Response Keys
```typescript
export const ResponseKeys = [
// Description is a good at a glance key, and stays at the top.
'description',
// Headers are a common key, and should be at the top.
'headers',
// Schema and content are the core shape of the response.
'schema', // Swagger 2.0
'content', // OpenAPI 3.0+
// Examples are of the schema, and should be directly below the schema.
'examples', // Swagger 2.0
// Links are programatic ways to link responses together.
'links', // OpenAPI 3.0+
] as const;
```
## Security Scheme Keys
```typescript
export const SecuritySchemeKeys = [
// Good at a glance keys.
'name',
'description',
// The primary type of this security scheme
'type',
'in',
'scheme',
// If scheme is bearer, bearerFormat is the format of the bearer token.
// Should be directly below scheme.
'bearerFormat',
// If scheme is openIdConnect, openIdConnectUrl is the URL of the OpenID Connect server.
'openIdConnectUrl',
// Flows are the different ways to authenticate with this security scheme.
'flows', // OpenAPI 3.0+
'flow', // Swagger 2.0
'authorizationUrl', // Swagger 2.0
'tokenUrl', // Swagger 2.0
'scopes', // Swagger 2.0
] as const;
```
## OAuth Flow Keys
```typescript
export const OAuthFlowKeys = [
// Authorization URL is where the client can get an authorization code.
'authorizationUrl',
// Token URL is where the client can get a token.
'tokenUrl',
// Refresh URL is where the client can refresh a token.
'refreshUrl',
// Scopes are the different scopes that can be used with this security scheme.
'scopes',
] as const;
```
## Server Keys
```typescript
export const ServerKeys = [
// Name first because obviously.
'name', // OpenAPI 3.2+
// Description so you know what you are looking at.
'description',
// URL is the URL of the server.
'url',
// Variables are the different variables that are present in the URL.
'variables',
] as const;
```
## Server Variable Keys
```typescript
export const ServerVariableKeys = [
// Description so you know what you are looking at.
'description',
// Default is the default value of the variable when that variable is not specified.
// IMO this should be optional, but I was not consulted.
'default',
// Enum is a static list of allowed values for the variable.
'enum',
] as const;
```
## Tag Keys
```typescript
export const TagKeys = [
// Name first because obviously.
'name',
// Description so you know what you are looking at.
'description',
// External docs should be like an extension of the description.
'externalDocs',
] as const;
```
## External Documentation Keys
```typescript
// The only sane key order, fight me.
export const ExternalDocsKeys = [
'description',
'url',
] as const;
```
## Webhook Keys
```typescript
// This seems like an obvious order given out running philosophy.
export const WebhookKeys = [
'summary',
'operationId',
'description',
'deprecated',
'tags',
'security',
'servers',
'parameters',
'requestBody',
'responses',
'callbacks',
] as const;
```
## Path Item Keys
```typescript
// Short blocks at the top, long at the bottom.
export const PathItemKeys = [
'$ref',
'summary',
'description',
'servers',
'parameters',
'get',
'put',
'post',
'delete',
'options',
'head',
'patch',
'trace',
] as const;
```
## Request Body Keys
```typescript
// Simple/short first
export const RequestBodyKeys = [
'description',
'required',
'content',
] as const;
```
## Media Type Keys
```typescript
// These are a bit trickier, all seem rather long in context.
// I'll with this order since it seems less to more complex.
export const MediaTypeKeys = [
'schema',
'example',
'examples',
'encoding',
] as const;
```
## Encoding Keys
```typescript
export const EncodingKeys = [
// Content type is just MIME type.
'contentType',
// Style, explode, and allowReserved are simple string or boolean values.
'style',
'explode',
'allowReserved',
// Headers is longer, put it at the bottom.
'headers',
] as const;
```
## Header Keys
```typescript
export const HeaderKeys = [
// Description is a good at a glance key, and stays at the top.
'description',
'required',
'deprecated',
'schema',
'content',
'type',
'format',
'style',
'explode',
'enum',
'default',
'example',
'examples',
// Array keys grouped together
'items',
'collectionFormat',
// Array constraints grouped together
'maxItems',
'minItems',
'uniqueItems',
// Numeric constraints grouped together
'minimum',
'multipleOf',
'exclusiveMinimum',
'maximum',
'exclusiveMaximum',
// String constraints grouped together
'pattern',
'minLength',
'maxLength',
] as const;
```
## Link Keys
```typescript
export const LinkKeys = [
'operationId',
'description',
'server',
'operationRef',
'parameters',
'requestBody',
] as const;
```
## Example Keys
```typescript
export const ExampleKeys = [
'summary',
'description',
'value',
'externalValue',
] as const;
```
## Discriminator Keys
```typescript
// Discriminator keys in preferred order (OpenAPI 3.0+)
export const DiscriminatorKeys = [
'propertyName',
'mapping',
] as const;
```
## XML Keys
```typescript
// XML keys in preferred order (OpenAPI 3.0+)
export const XMLKeys = [
'name',
'namespace',
'prefix',
'attribute',
'wrapped',
] as const;
```

View File

@@ -131,6 +131,8 @@ api.yaml # Single file with everything
The plugin automatically sorts OpenAPI keys in the recommended order:
> 📖 **Complete Key Reference**: For a comprehensive reference of all keys, their ordering, and detailed reasoning, see [KEYS.md](./KEYS.md).
### Top-level keys:
1. `openapi` / `swagger`
2. `info`

View File

@@ -29,6 +29,7 @@
"useConst": "error"
},
"suspicious": {
"noExplicitAny": "off",
"noConsole": "off",
"noVar": "error"
}

View File

@@ -48,7 +48,7 @@ export function getVendorExtensions(customVendorModules?: VendorModule[]): Recor
for (const vendorModule of modulesToLoad) {
try {
if (vendorModule && vendorModule.extensions) {
if (vendorModule?.extensions) {
for (const [context, contextFunction] of Object.entries(vendorModule.extensions)) {
if (typeof contextFunction === 'function') {
// Create context-specific before/after functions

View File

@@ -359,7 +359,7 @@ function sortResponseCodes(a: string, b: string): number {
const aNum = parseInt(a);
const bNum = parseInt(b);
if (!isNaN(aNum) && !isNaN(bNum)) {
if (!Number.isNaN(aNum) && !Number.isNaN(bNum)) {
return aNum - bNum;
}