Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
---
name: add-oas-support
description: Add support for a new OpenAPI Specification version to Swagger UI
args:
version:
description: "OpenAPI version to add support for (e.g., 3.2, 4.0)"
required: true
type: string
type:
description: "Version type: 'major' for new major version, 'minor' for patch/minor"
required: false
type: string
default: "minor"
---
# Add OpenAPI Specification Version Support
This skill guides you through adding support for a new OpenAPI Specification (OAS) version to Swagger UI, following the established architectural patterns from OAS 3.1 implementation.
---
## π Quick Reference
> **New to this skill?** Skip to [Prerequisites](#prerequisites) for the full guide.
> **Already familiar?** Use this quick reference for rapid development.
### π Quick Start
```bash
# Major version (e.g., OAS 4.0)
/add-oas-support --version 4.0 --type major
# Minor version (e.g., OAS 3.2)
/add-oas-support --version 3.2 --type minor
```
### π Essential Checklist
#### Phase 1: Specification Analysis β MOST IMPORTANT
- [ ] Access official spec at **https://spec.openapis.org/oas/v{VERSION}/**
- [ ] Use WebFetch to analyze spec systematically
- [ ] Create specification change document
- [ ] Map changes to components using [mapping table](#step-1d-map-specification-changes-to-swagger-ui-components)
#### Phase 2: Implementation
- [ ] Create plugin directory structure
- [ ] Implement version detection (`isOAS{VERSION}`)
- [ ] Create selector factories
- [ ] Implement components for each spec change
- [ ] Wrap existing components if modified
- [ ] Register plugin in preset (LAST position)
#### Phase 3: Testing & Documentation
- [ ] Add unit tests
- [ ] Add E2E tests with spec examples
- [ ] Update documentation
- [ ] Verify build & run full test suite
### π WebFetch Queries for Spec Analysis
```javascript
// 1. Fetch main spec differences
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"List all new top-level fields, modified fields, and removed fields compared to version {PREV_VERSION}"
)
// 2. Analyze new feature
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"Describe the '{NEW_FIELD}' field: structure, type, purpose, required/optional, and provide examples"
)
// 3. Check JSON Schema version
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"What JSON Schema version does this use? List changes from {PREV_JSON_SCHEMA_VERSION}"
)
// 4. Security schemes
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"List all security scheme types, highlighting new or changed types"
)
```
### πΊοΈ Spec Change β Component Mapping (Quick)
| Spec Change | Component Action | File Location |
|-------------|------------------|---------------|
| **New top-level field (Object)** | Create new component | `components/{field}.jsx` |
| **New top-level field (String)** | Add to info or create display | `components/{field}.jsx` |
| **Modified Info object** | Wrap Info component | `wrap-components/info.jsx` |
| **New info subfield** | Create component + wrap Info | `components/info-{field}.jsx` |
| **New auth type** | Create auth component | `components/auth/{type}.jsx` |
| **Modified security** | Wrap auth selector | `auth-extensions/wrap-selectors.js` |
| **New operation field** | Extend Operation/selector | `spec-extensions/selectors.js` |
| **JSON Schema keyword** | Create keyword component | `json-schema-{V}-extensions/` |
| **JSON Schema version** | Create entire new plugin | `plugins/json-schema-{V}/` |
See [full mapping table with examples](#step-1d-map-specification-changes-to-swagger-ui-components) for detailed guidance.
### π Component Implementation Pattern
**For each new feature:**
1. **Check spec first:**
```javascript
WebFetch(url, "Describe {field} structure, type, examples")
```
2. **Create selector:**
```javascript
// spec-extensions/selectors.js
export const selectNewField = createSelector(
(state) => state,
(state) => state.getIn(["spec", "json", "newField"])
)
```
3. **Create component:**
```javascript
// components/new-field.jsx
const NewField = ({ specSelectors, getComponent }) => {
const data = specSelectors.selectNewField()
if (!data) return null
return <div>{/* render based on spec structure */}</div>
}
```
4. **Register in plugin:**
```javascript
// index.js
components: { NewField },
statePlugins: {
spec: {
selectors: {
selectNewField: createOnlyOAS{V}Selector(selectNewField)
}
}
}
```
5. **Add tests** (unit + E2E with spec examples)
### β οΈ Common Pitfalls
1. β **Guessing field structure** β β
Always check spec with WebFetch
2. β **Forgetting @prettier pragma** β β
Add to all new files
3. β **Wrong plugin load order** β β
New plugin LAST in preset
4. β **Using semicolons** β β
No semicolons (project convention)
5. β **Single quotes** β β
Use double quotes
6. β **Skipping spec examples** β β
Use as test fixtures
7. β **Hardcoded assumptions** β β
Verify everything in spec
8. β **`props.Ori` in ComponentWrapper** β β
Use `props.originalComponent` (see Pattern 3)
9. β **Copying entire Info component for minor version** β β
Use OpenAPIVersion wrapper + `getComponent("OAS{PREV}Info")`
10. β **OAS{VERSION} logic in OAS{PREV} plugin** β β
Each version's logic lives in its own plugin
11. β **`isOAS{PREV}` wrapper on minor version** β β
Only add if regex overlaps; minor bumps don't need it
12. β **New HTTP method in core `validOperationMethods`** β β
Add to `OPERATION_METHODS` + wrap with `createOnlyOAS{VERSION}SelectorWrapper`
13. β **Assuming new OAS meta-schema = new JSON Schema dialect** β β
Verify β OAS 3.2 still uses JSON Schema 2020-12
14. β **Inline version guard in wrap-components** β β
Always use `createOnlyOAS{VERSION}ComponentWrapper`; never write `(Original, system) => (props) => { if (...isOAS{VERSION}...) }` by hand β even expanding an existing guard (`isOAS31 || isOAS32`) is wrong; add dedicated wrap-components in the new plugin instead
### β
Pre-Submit Checklist
- [ ] All spec changes mapped to components
- [ ] All components reference spec in comments
- [ ] Tests use examples from spec
- [ ] Version detection regex correct
- [ ] Plugin loaded last in preset
- [ ] All tests passing (lint + unit + E2E)
- [ ] Build completes successfully
- [ ] Documentation updated
- [ ] No semicolons, double quotes everywhere
- [ ] @prettier pragma in all new files
### π Quick Links
- **OAS Specs:** https://spec.openapis.org/
- **OAS 3.1:** https://spec.openapis.org/oas/v3.1.0/
- **OAS 3.0:** https://spec.openapis.org/oas/v3.0.3/
- **JSON Schema:** https://json-schema.org/
- **CLAUDE.md:** Complete codebase guide
### π Full Guide Navigation
Need detailed guidance? Jump to these sections:
- **[Prerequisites](#prerequisites)** - Setup and requirements
- **[Architecture Overview](#architecture-overview)** - Plugin system explained
- **[Key Design Patterns](#key-design-patterns-from-oas-31)** - Code patterns from OAS 3.1
- **[Step 1: Analyze Specification](#step-1-analyze-new-oas-version-specification)** - β Complete spec analysis workflow
- [1D: Mapping Table](#step-1d-map-specification-changes-to-swagger-ui-components) - 15+ spec change types
- [1E: Real Examples](#step-1e-examples-oas-31-specification-changes--components) - 6 detailed examples
- [1H: WebFetch Workflow](#step-1h-using-webfetch-to-analyze-specifications) - Spec analysis with WebFetch
- **[Step 2-17: Implementation](#step-2-create-plugin-directory-structure)** - Complete implementation steps
- **[Practical Workflow](#practical-workflow-from-spec-to-components)** - End-to-end example
- **[Best Practices](#best-practices-iterative-spec-driven-development)** - Spec-driven development
- **[Common Pitfalls](#common-pitfalls)** - What to avoid
- **[Final Checklist](#final-checklist)** - Pre-submission verification
---
**Remember: The specification at https://spec.openapis.org/ is your source of truth!**
---
## Prerequisites
Before starting, ensure:
- You understand the Swagger UI plugin architecture
- You've read the CLAUDE.md guide
- You have the OAS specification document for the new version
- All tests pass: `npm test`
## Architecture Overview
Adding OAS version support involves:
1. **Plugin Creation** - New plugin following established patterns
2. **Version Detection** - Regex-based version identifier
3. **Selector Factories** - Conditional logic for version-specific features
4. **Component Implementation** - New/wrapped components for rendering
5. **State Management** - Redux selectors/actions for new features
6. **Lifecycle Hooks** - afterLoad modifications
7. **Preset Registration** - Plugin loading order
8. **Testing** - Unit and E2E tests
9. **Documentation** - Update guides and README
## Key Design Patterns (from OAS 3.1)
### Pattern 1: Version Detection
```javascript
export const isOAS{VERSION} = (jsSpec) => {
const oasVersion = jsSpec.get("openapi")
return (
typeof oasVersion === "string" && /^{MAJOR}\.{MINOR}\.(?:[1-9]\d*|0)$/.test(oasVersion)
)
}
```
### Pattern 2: Selector Factories
**createOnlyOAS{VERSION}Selector** - Features exclusive to this version:
```javascript
export const createOnlyOAS{VERSION}Selector =
(selector) =>
(state, ...args) =>
(system) => {
if (system.getSystem().specSelectors.isOAS{VERSION}()) {
const selectedValue = selector(state, ...args)
return typeof selectedValue === "function"
? selectedValue(system)
: selectedValue
} else {
return null
}
}
```
**createOnlyOAS{VERSION}SelectorWrapper** - Override previous versions:
```javascript
export const createOnlyOAS{VERSION}SelectorWrapper =
(selector) =>
(oriSelector, system) =>
(state, ...args) => {
if (system.getSystem().specSelectors.isOAS{VERSION}()) {
const selectedValue = selector(state, ...args)
return typeof selectedValue === "function"
? selectedValue(oriSelector, system)
: selectedValue
} else {
return oriSelector(...args)
}
}
```
### Pattern 3: Component Wrappers
**β οΈ IMPORTANT: `originalComponent` prop name, not `Ori`**
`createOnlyOAS{VERSION}ComponentWrapper` passes the original component as `originalComponent` in props (NOT `Ori` β that's the OAS3/OAS30ComponentWrapFactory convention). Use `const { originalComponent: Ori } = props` to access it.
**Pattern A β Reuse previous version's component via getComponent:**
```javascript
const ComponentWrapper = createOnlyOAS{VERSION}ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const OAS{PREV_VERSION}Component = system.getComponent("OAS{PREV_VERSION}Component", true)
return <OAS{PREV_VERSION}Component />
})
```
**Pattern B β Render original component with extra props (e.g. version badge):**
```javascript
// Use `originalComponent` (NOT `Ori`) β that's the prop name createOnlyOAS{VERSION}ComponentWrapper passes
const OpenAPIVersionWrapper = createOnlyOAS{VERSION}ComponentWrapper((props) => {
const { originalComponent: Ori } = props
return <Ori oasVersion="{VERSION}" />
})
```
### Pattern 4: Function Wrapping
```javascript
export const wrapOAS{VERSION}Fn = (fn, system) => {
const { fn: systemFn, specSelectors } = system
return Object.fromEntries(
Object.entries(fn).map(([name, newImpl]) => {
const oriImpl = systemFn[name]
const impl = (...args) =>
specSelectors.isOAS{VERSION}()
? newImpl(...args)
: typeof oriImpl === "function"
? oriImpl(...args)
: undefined
return [name, impl]
})
)
}
```
## Implementation Steps
### Step 1: Analyze New OAS Version Specification
**CRITICAL: Always start by reviewing the official specification at https://spec.openapis.org/**
#### 1A. Access the Official Specification
**Primary Resource:** https://spec.openapis.org/
**Available Versions:**
- OAS 2.0: https://spec.openapis.org/oas/v2.0/
- OAS 3.0.x: https://spec.openapis.org/oas/v3.0.3/
- OAS 3.1.x: https://spec.openapis.org/oas/v3.1.0/
- Future versions will follow the pattern: https://spec.openapis.org/oas/v{MAJOR}.{MINOR}.{PATCH}/
**What to download:**
1. The specification document (usually in HTML or Markdown format)
2. JSON Schema definitions (if available)
3. Example specification files
4. Change log or migration guide (if available)
#### 1B. Systematic Specification Analysis
Use the WebFetch tool to retrieve and analyze the specification:
```bash
# Example for analyzing OAS 4.0 (hypothetical)
WebFetch("https://spec.openapis.org/oas/v4.0.0/", "List all new top-level fields, objects, and keywords introduced in this version compared to OAS 3.1")
```
**Read these sections carefully:**
1. **Version Number Section**
- Confirm the exact version format (e.g., "4.0.0", "3.2.0")
- Note any version detection changes
2. **OpenAPI Object (Root Level)**
- New top-level fields (e.g., `webhooks` in OAS 3.1)
- Modified existing fields
- Removed/deprecated fields
3. **Info Object**
- New fields (e.g., `summary` in OAS 3.1)
- Changes to `license`, `contact`, etc.
4. **Paths Object & Operations**
- New operation-level fields
- Changes to parameters, request bodies, responses
- New callback syntax or features
5. **Components Object**
- New component types
- Schema changes (especially JSON Schema version)
- New security schemes
6. **Security Object**
- New authentication types (e.g., `mutualTLS` in OAS 3.1)
- Changes to OAuth2 flows
- New security-related fields
7. **Schema Object**
- JSON Schema version change (critical!)
- New keywords (e.g., `examples` vs `example`)
- Type system changes
#### 1C. Create Specification Change Document
Create a structured document listing all changes:
**Template:**
```markdown
# OpenAPI {VERSION} Specification Analysis
## Version Information
- Version: {MAJOR}.{MINOR}.{PATCH}
- Release Date: YYYY-MM-DD
- Previous Version: {PREVIOUS_VERSION}
- JSON Schema Version: Draft {VERSION} or {YEAR}
## New Top-Level Fields
1. **`newField`** (Type: Object)
- Location: Root level
- Purpose: Description from spec
- Required: Yes/No
- Example from spec: {...}
- Swagger UI Impact: Needs new component in BaseLayout
## Modified Existing Fields
1. **`info`**
- New subfields: `summary`, `newField`
- Changed behavior: Description
- Swagger UI Impact: Need to wrap Info component
## New Operation-Level Features
1. **`newOperationField`**
- Purpose: Description
- Swagger UI Impact: Extend Operation component
## New Authentication Types
1. **`newAuthType`**
- Type name: "newAuthType"
- Fields: {...}
- Swagger UI Impact: New auth component needed
## JSON Schema Changes
- Version change: Draft 2020-12 β Draft 2024
- New keywords: `keyword1`, `keyword2`
- Removed keywords: `oldKeyword`
- Swagger UI Impact: New json-schema plugin needed
## Breaking Changes
1. Removed `x-deprecated-field`
2. Changed behavior of `existingField`
3. Required fields now enforced
## Deprecated Features
1. `oldField` - Use `newField` instead
2. `oldAuthType` - Use `newAuthType` instead
## Examples from Specification
- Link to example files
- Notable edge cases to test
```
#### 1D. Map Specification Changes to Swagger UI Components
**CRITICAL MAPPING GUIDE:** Use this table to determine what components to create/modify:
| Spec Change Type | Swagger UI Component Type | Location | Example from OAS 3.1 |
|-----------------|---------------------------|----------|---------------------|
| **New Top-Level Field (Object)** | New standalone component | `components/{feature-name}.jsx` | `webhooks` β `Webhooks.jsx` |
| **New Top-Level Field (String/Primitive)** | Add to existing layout component or create info component | `components/{field-name}.jsx` | `jsonSchemaDialect` β `JsonSchemaDialect.jsx` |
| **Modified Info Object** | Wrap Info component OR create OAS{VERSION}Info | `wrap-components/info.jsx` | `info.summary` β Wrapped InfoContainer |
| **New Info Subfield** | Create new component, render in Info | `components/info-{field}.jsx` | `info.license.identifier` β OAS31License |
| **Modified License/Contact** | Wrap component | `wrap-components/license.jsx` | `license.identifier` β Wrapped License |
| **New Authentication Type** | New auth component | `components/auth/{type}.jsx` | `mutualTLS` β `MutualTLSAuth.jsx` |
| **Modified Security Scheme** | Wrap auth selector | `auth-extensions/wrap-selectors.js` | mutualTLS β `definitionsToAuthorize` wrapper |
| **New Operation Field** | Extend Operation or create plugin selector | `spec-extensions/selectors.js` | N/A in OAS 3.1 |
| **New Callback Feature** | Extend Callbacks component or wrap | `wrap-components/callbacks.jsx` | Enhanced in OAS 3.1 |
| **New Parameter Type** | Extend Parameters component | `components/parameters/` | N/A in OAS 3.1 |
| **New Response Feature** | Extend Responses component | `components/responses/` | N/A in OAS 3.1 |
| **New Media Type Feature** | Modify media-type component or fn | `oas{PREV}-extensions/fn.js` | File upload detection changes |
| **JSON Schema Keyword** | New keyword component | `json-schema-{VERSION}-extensions/components/keywords/` | `example` β `JSONSchema202012KeywordExample.jsx` |
| **JSON Schema Version Change** | Entire new plugin + samples plugin | `plugins/json-schema-{VERSION}/` | JSON Schema 2020-12 plugin |
| **Modified Schema Rendering** | Wrap schema component | `wrap-components/model.jsx` | Models β OAS31Models wrapper |
| **Version Detection Field** | Update VersionPragmaFilter | `components/version-pragma-filter.jsx` | Updated for isOAS31 |
| **Deprecated Field** | Remove or add deprecation warning | Component showing warning | N/A in OAS 3.1 |
#### 1E. Examples: OAS 3.1 Specification Changes β Components
**Example 1: `webhooks` Top-Level Field**
**From Spec (OAS 3.1):**
```yaml
openapi: 3.1.0
webhooks:
newPet:
post:
requestBody:
description: Information about a new pet
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
responses:
'200':
description: Return a 200 status
```
**Mapping Decision:**
- Type: New top-level field (Object containing operations)
- Location in spec: Root level, same structure as `paths`
- Swagger UI component: New standalone component
- File: `src/core/plugins/oas31/components/webhooks.jsx`
- Rendering: Reuses Operations component to render webhook operations
- Selector: `selectWebhooks` in `spec-extensions/selectors.js`
- Layout integration: Added to BaseLayout or StandaloneLayout
**Example 2: `info.summary` Field**
**From Spec (OAS 3.1):**
```yaml
info:
title: My API
summary: A brief summary of the API # NEW in OAS 3.1
description: Detailed description...
version: 1.0.0
```
**Mapping Decision:**
- Type: New subfield in existing Info object
- Location: `info.summary`
- Swagger UI component: Wrap existing Info component
- File: `src/core/plugins/oas31/wrap-components/info.jsx` + `src/core/plugins/oas31/components/info.jsx`
- Selector: `selectInfoSummary` (if needed)
- Rendering: Display summary above or below title
**Example 3: `mutualTLS` Authentication Type**
**From Spec (OAS 3.1):**
```yaml
components:
securitySchemes:
myMutualTLS:
type: mutualTLS # NEW in OAS 3.1
description: Client certificate authentication
```
**Mapping Decision:**
- Type: New authentication scheme type
- Location: `components.securitySchemes[name].type`
- Swagger UI component: New auth component
- File: `src/core/plugins/oas31/components/auth/mutual-tls-auth.jsx`
- Selector wrapper: `auth-extensions/wrap-selectors.js` β `definitionsToAuthorize`
- Rendering: Display in authorize modal with certificate upload UI
**Example 4: `license.identifier` Field**
**From Spec (OAS 3.1):**
```yaml
info:
license:
name: Apache 2.0
identifier: Apache-2.0 # NEW in OAS 3.1 (SPDX ID)
url: https://www.apache.org/licenses/LICENSE-2.0.html
```
**Mapping Decision:**
- Type: New optional field in license object (alternative to url)
- Location: `info.license.identifier`
- Swagger UI component: Wrap License component + create selector
- File: `src/core/plugins/oas31/wrap-components/license.jsx` + selector in `spec-extensions/selectors.js`
- Logic: If `identifier` exists, construct URL from SPDX registry
- Rendering: Make license text a hyperlink to SPDX page
**Example 5: JSON Schema 2020-12 Keywords**
**From Spec (OAS 3.1):**
```yaml
# OAS 3.1 uses JSON Schema 2020-12 which includes:
components:
schemas:
Pet:
type: object
properties:
name:
type: string
example: Fluffy # Changed from 'examples' array in OAS 3.0
xml: # Now a JSON Schema keyword, not OAS-specific
name: pet
externalDocs: # Now a JSON Schema keyword
url: https://example.com
discriminator: # Now a JSON Schema keyword
propertyName: petType
```
**Mapping Decision:**
- Type: JSON Schema version upgrade (Draft 7 β 2020-12)
- Location: Schema objects throughout spec
- Swagger UI components: Entire new plugin
- Files:
- `src/core/plugins/json-schema-2020-12/` (main plugin)
- `src/core/plugins/json-schema-2020-12-samples/` (sample generation)
- `src/core/plugins/json-schema-2020-12/components/keywords/example.jsx`
- `src/core/plugins/json-schema-2020-12/components/keywords/xml.jsx`
- `src/core/plugins/json-schema-2020-12/components/keywords/discriminator.jsx`
- `src/core/plugins/json-schema-2020-12/components/keywords/external-docs.jsx`
- Integration: OAS31 plugin extends JSON Schema 2020-12 keywords in `json-schema-2020-12-extensions/`
**Example 6: `jsonSchemaDialect` Top-Level Field**
**From Spec (OAS 3.1):**
```yaml
openapi: 3.1.0
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base # NEW in OAS 3.1
```
**Mapping Decision:**
- Type: New top-level field (String/URI)
- Location: Root level
- Swagger UI component: Simple display component
- File: `src/core/plugins/oas31/components/json-schema-dialect.jsx`
- Selector: `selectJsonSchemaDialectField` in `spec-extensions/selectors.js`
- Rendering: Show in info section with warning if non-default
#### 1F. Specification Analysis Checklist
Use WebFetch and Read tools to systematically check:
- [ ] **Accessed official spec at https://spec.openapis.org/**
- [ ] **Downloaded specification document**
- [ ] **Read version number section**
- [ ] **Analyzed OpenAPI Object (root level) changes**
- [ ] **Reviewed Info Object modifications**
- [ ] **Checked Paths/Operations changes**
- [ ] **Examined Components Object updates**
- [ ] **Identified Security Scheme changes**
- [ ] **Analyzed Schema Object / JSON Schema version**
- [ ] **Listed all new top-level fields**
- [ ] **Listed all modified existing fields**
- [ ] **Documented breaking changes**
- [ ] **Identified deprecated features**
- [ ] **Found example specifications**
- [ ] **Created specification change document**
- [ ] **Mapped changes to Swagger UI components using table above**
- [ ] **Prioritized implementation order**
#### 1G. Questions to Answer During Analysis
**Version Type:**
- Is this a major version (4.0) or minor version (3.2)?
- Are there breaking changes?
- Is backward compatibility maintained?
**JSON Schema:**
- Does it use a new JSON Schema version?
- What new keywords are available?
- Are there removed/deprecated keywords?
**Top-Level Changes:**
- What new top-level fields are added?
- Which existing fields have new subfields?
- Any removed top-level fields?
**Operation Changes:**
- Are there new operation-level features?
- Changes to parameters, request bodies, responses?
- New callback or link syntax?
**Security Changes:**
- What authentication types are new/changed?
- OAuth2 flow modifications?
- New security requirements?
**Media Type Changes:**
- Are there new media type features?
- File upload handling changes?
- Encoding changes?
**Component Mapping:**
- Which components need to be created from scratch?
- Which existing components need to be wrapped?
- What selectors are required?
- Are new plugins needed (e.g., for JSON Schema)?
**Implementation Priority:**
- What's the critical path for basic rendering?
- Which features can be added incrementally?
- What has the highest user impact?
### Step 1H: Using WebFetch to Analyze Specifications
**Always use WebFetch tool to retrieve and analyze the specification systematically.**
**Step-by-step WebFetch workflow:**
1. **Fetch the specification landing page:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"Provide a summary of this OpenAPI Specification version, including links to the full specification document and any change logs or migration guides."
)
```
2. **Analyze the specification structure:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"List all top-level fields in the OpenAPI Object for this version. For each field, indicate if it is new, modified, or unchanged from version {PREVIOUS_VERSION}."
)
```
3. **Deep dive into new features:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"Describe the '{NEW_FIELD}' field in detail: its type, structure, purpose, whether it's required, and provide examples from the specification."
)
```
4. **Analyze security changes:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"List all security scheme types supported in this version. Highlight any new or changed security scheme types compared to {PREVIOUS_VERSION}."
)
```
5. **Check JSON Schema compatibility:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"What version of JSON Schema does this OpenAPI Specification version use? List any specific dialect or modifications to JSON Schema."
)
```
6. **Find example specifications:**
```javascript
WebFetch(
"https://spec.openapis.org/oas/v{VERSION}/",
"Provide links to example OpenAPI specification files for this version, particularly examples that demonstrate new features."
)
```
**Real example from analyzing OAS 3.1:**
```javascript
// 1. Initial analysis
WebFetch(
"https://spec.openapis.org/oas/v3.1.0/",
"List all new fields and features introduced in OpenAPI 3.1.0 compared to OpenAPI 3.0.3"
)
// Response would include:
// - webhooks field (new top-level)
// - info.summary (new)
// - info.license.identifier (new)
// - jsonSchemaDialect (new top-level)
// - mutualTLS security scheme type (new)
// - JSON Schema 2020-12 (changed from Draft 7)
// 2. Deep dive into webhooks
WebFetch(
"https://spec.openapis.org/oas/v3.1.0/",
"Explain the 'webhooks' field in detail: structure, purpose, and provide a complete example"
)
// 3. Understand JSON Schema changes
WebFetch(
"https://spec.openapis.org/oas/v3.1.0/",
"What are the differences between JSON Schema Draft 7 (used in OAS 3.0) and JSON Schema 2020-12 (used in OAS 3.1) that affect schema definitions?"
)
```
**Pro tip:** Use multiple targeted queries rather than one broad query. This gives more precise information for each component you need to implement.
### Step 2: Create Plugin Directory Structure
**Directory:** `src/core/plugins/oas{VERSION_NUMBER}/`
**Files to Create:**
```
src/core/plugins/oas{VERSION_NUMBER}/
βββ index.js # Main plugin export
βββ fn.js # Selector/function factories
βββ selectors.js # Version-specific selectors (if needed)
βββ after-load.js # Lifecycle hook
βββ auth-extensions/ # Authentication features
β βββ wrap-selectors.js
βββ oas{PREVIOUS_VERSION}-extensions/ # Extensions to previous version
β βββ fn.js
βββ spec-extensions/ # Spec-level features
β βββ selectors.js
β βββ wrap-selectors.js
βββ components/ # New components
β βββ version-pragma-filter.jsx
β βββ {new-feature-1}.jsx
β βββ auth/
β βββ {new-auth-type}.jsx
βββ wrap-components/ # Component wrappers
β βββ info.jsx
β βββ license.jsx
β βββ {existing-component}.jsx
βββ json-schema-{VERSION}-extensions/ # If new JSON Schema version
βββ fn.js
βββ components/keywords/
βββ wrap-components/keywords/
```
**Create the directories:**
```bash
mkdir -p src/core/plugins/oas{VERSION_NUMBER}/{auth-extensions,oas{PREVIOUS_VERSION}-extensions,spec-extensions,components/auth,wrap-components}
```
### Step 3: Implement Version Detection (fn.js)
**File:** `src/core/plugins/oas{VERSION_NUMBER}/fn.js`
**Template:**
```javascript
/**
* @prettier
*/
/**
* Detects if a spec is OpenAPI {MAJOR}.{MINOR}.x
*/
export const isOAS{VERSION} = (jsSpec) => {
const oasVersion = jsSpec.get("openapi")
return (
typeof oasVersion === "string" &&
/^{MAJOR}\.{MINOR}\.(?:[1-9]\d*|0)$/.test(oasVersion)
)
}
/**
* Creates a selector that only returns a value for OAS {VERSION} specs
*/
export const createOnlyOAS{VERSION}Selector =
(selector) =>
(state, ...args) =>
(system) => {
if (system.getSystem().specSelectors.isOAS{VERSION}()) {
const selectedValue = selector(state, ...args)
return typeof selectedValue === "function"
? selectedValue(system)
: selectedValue
} else {
return null
}
}
/**
* Creates a selector wrapper that uses new impl for OAS {VERSION}, falls back to original
*/
export const createOnlyOAS{VERSION}SelectorWrapper =
(selector) =>
(oriSelector, system) =>
(state, ...args) => {
if (system.getSystem().specSelectors.isOAS{VERSION}()) {
const selectedValue = selector(state, ...args)
return typeof selectedValue === "function"
? selectedValue(oriSelector, system)
: selectedValue
} else {
return oriSelector(...args)
}
}
/**
* Creates a system-aware selector
*/
export const createSystemSelector =
(selector) =>
(state, ...args) =>
(system) => {
const selectedValue = selector(state, system, ...args)
return typeof selectedValue === "function"
? selectedValue(system)
: selectedValue
}
/**
* Creates a component wrapper that only renders for OAS {VERSION}.
* When active, passes `originalComponent` (the unwrapped original) and
* `getSystem` as extra props. Access the original via:
* const { originalComponent: Ori } = props
* NOT via `props.Ori` β that's the OAS3/OAS30ComponentWrapFactory convention.
*/
export const createOnlyOAS{VERSION}ComponentWrapper =
(Component) => (Original, system) => (props) => {
if (system.specSelectors.isOAS{VERSION}()) {
return (
<Component
{...props}
originalComponent={Original}
getSystem={system.getSystem}
/>
)
}
return <Original {...props} />
}
/**
* Wraps functions to conditionally use OAS {VERSION} implementations
*/
export const wrapOAS{VERSION}Fn = (fn, system) => {
const { fn: systemFn, specSelectors } = system
return Object.fromEntries(
Object.entries(fn).map(([name, newImpl]) => {
const oriImpl = systemFn[name]
const impl = (...args) =>
specSelectors.isOAS{VERSION}()
? newImpl(...args)
: typeof oriImpl === "function"
? oriImpl(...args)
: undefined
return [name, impl]
})
)
}
```
### Step 4: Create Main Plugin Export (index.js)
**File:** `src/core/plugins/oas{VERSION_NUMBER}/index.js`
**Template:**
```javascript
/**
* @prettier
*/
import afterLoad from "./after-load.js"
import {
isOAS{VERSION},
createSystemSelector,
createOnlyOAS{VERSION}Selector,
createOnlyOAS{VERSION}SelectorWrapper,
createOnlyOAS{VERSION}ComponentWrapper,
} from "./fn.js"
// Import spec extensions
import * as specExtensionsSelectors from "./spec-extensions/selectors.js"
import * as specExtensionsWrapSelectors from "./spec-extensions/wrap-selectors.js"
// Import auth extensions
import * as authExtensionsWrapSelectors from "./auth-extensions/wrap-selectors.js"
// Import OAS{PREVIOUS_VERSION} extensions
import * as oas{PREVIOUS_VERSION}ExtensionsFn from "./oas{PREVIOUS_VERSION}-extensions/fn.js"
// Import components
import VersionPragmaFilter from "./components/version-pragma-filter.jsx"
// Import new feature components here
// Import wrap components
import InfoWrapper from "./wrap-components/info.jsx"
// Import other wrappers here
/**
* OpenAPI {MAJOR}.{MINOR} Plugin
*
* Adds support for OpenAPI Specification {MAJOR}.{MINOR}.x
*
* This plugin should be loaded AFTER:
* - oas{PREVIOUS_VERSION} plugin
* - json-schema-{JSON_SCHEMA_VERSION} plugin (if applicable)
*
* It wraps and overrides components/selectors from previous versions.
*/
const OpenAPI{VERSION}Plugin = ({ fn }) => {
return {
fn: {
oas{VERSION_NUMBER}: {
isOAS{VERSION},
createSystemSelector,
createOnlyOAS{VERSION}Selector,
createOnlyOAS{VERSION}SelectorWrapper,
createOnlyOAS{VERSION}ComponentWrapper,
},
},
components: {
// New components
OAS{VERSION}VersionPragmaFilter: VersionPragmaFilter,
// Add new feature components here
},
wrapComponents: {
// Wrapped components
VersionPragmaFilter: (Ori, system) =>
system.specSelectors.isOAS{VERSION}()
? system.getComponent("OAS{VERSION}VersionPragmaFilter", true)
: Ori,
InfoContainer: InfoWrapper,
// Add other wrappers here
},
statePlugins: {
spec: {
selectors: {
// Add version detection selector
isOAS{VERSION}: createSystemSelector(specExtensionsSelectors.selectIsOAS{VERSION}),
// Add new feature selectors here
},
wrapSelectors: {
// Wrap previous version selectors if needed
...specExtensionsWrapSelectors,
},
},
auth: {
wrapSelectors: {
// Add auth extensions if needed
...authExtensionsWrapSelectors,
},
},
oas{VERSION_NUMBER}: {
selectors: {
// Plugin-specific selectors
},
},
},
afterLoad,
}
}
export default OpenAPI{VERSION}Plugin
```
### Step 5: Implement Spec Extensions
**File:** `src/core/plugins/oas{VERSION_NUMBER}/spec-extensions/selectors.js`
**Template:**
```javascript
/**
* @prettier
*/
import { createSelector } from "reselect"
import { isOAS{VERSION} } from "../fn.js"
/**
* Detects if the current spec is OAS {VERSION}
*/
export const selectIsOAS{VERSION} = (state, system) => () => {
const spec = system.specSelectors.specJson()
return isOAS{VERSION}(spec)
}
// Add selectors for new OAS {VERSION} fields here
// Example:
// export const selectNewField = createSelector(
// (state) => state,
// (state) => {
// const spec = state.getIn(["spec", "json"])
// return spec.get("newField")
// }
// )
```
**File:** `src/core/plugins/oas{VERSION_NUMBER}/spec-extensions/wrap-selectors.js`
**β οΈ Minor version: DON'T wrap `isOAS{PREV}` unless the regex actually matches both versions.** For example, OAS 3.1's `isOAS31` regex (`/^3\.1\./`) will never match `3.2.x`, so wrapping it to return `false` is dead code. Only add the `isOAS{PREV}` override if the previous version's detection regex would incorrectly match the new version.
**Template:**
```javascript
/**
* @prettier
*/
import { createOnlyOAS{VERSION}SelectorWrapper } from "../fn.js"
// Ensure OAS {VERSION} specs are recognized as OAS 3.x (needed when major version number didn't change)
export const isOAS3 =
(oriSelector, system) =>
(state, ...args) => {
const isOAS{VERSION} = system.specSelectors.isOAS{VERSION}()
return isOAS{VERSION} || oriSelector(...args)
}
// ONLY add isOAS{PREV} wrapper if the previous version's regex could match this new version.
// For a minor version bump (e.g. 3.1 β 3.2), the previous regex won't match, so DON'T add this.
// export const isOAS{PREV} = createOnlyOAS{VERSION}SelectorWrapper((state) => () => false)
```
### Step 6: Create Version Pragma Filter Component
**File:** `src/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx`
**Template:**
```javascript
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"
const OAS{VERSION}VersionPragmaFilter = ({ bypass, isSwagger2, isOAS3, isOAS{PREVIOUS_VERSION}, isOAS{VERSION} }) => {
// Handle version detection logic
const isAmbiguous =
(isSwagger2 && isOAS3) ||
(isSwagger2 && isOAS{PREVIOUS_VERSION}) ||
(isSwagger2 && isOAS{VERSION}) ||
(isOAS3 && isOAS{PREVIOUS_VERSION}) ||
(isOAS3 && isOAS{VERSION}) ||
(isOAS{PREVIOUS_VERSION} && isOAS{VERSION})
const isMissing = !isSwagger2 && !isOAS3 && !isOAS{PREVIOUS_VERSION} && !isOAS{VERSION}
if (bypass) {
return null
}
if (isAmbiguous) {
return (
<div className="version-pragma">
<div className="version-pragma__message version-pragma__message--ambiguous">
<div>
<h3>Unable to render this document, as it contains multiple OpenAPI version fields.</h3>
</div>
</div>
</div>
)
}
if (isMissing) {
return (
<div className="version-pragma">
<div className="version-pragma__message version-pragma__message--missing">
<div>
<h3>Unable to render this document, as it doesn't contain an OpenAPI version field.</h3>
</div>
</div>
</div>
)
}
return null
}
OAS{VERSION}VersionPragmaFilter.propTypes = {
bypass: PropTypes.bool,
isSwagger2: PropTypes.bool.isRequired,
isOAS3: PropTypes.bool.isRequired,
isOAS{PREVIOUS_VERSION}: PropTypes.bool.isRequired,
isOAS{VERSION}: PropTypes.bool.isRequired,
}
OAS{VERSION}VersionPragmaFilter.defaultProps = {
bypass: false,
}
export default OAS{VERSION}VersionPragmaFilter
```
### Step 7: Implement New Feature Components
For each new feature in the OAS specification:
**Example: New Feature Component**
```javascript
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"
const NewFeature = ({ getComponent, specSelectors }) => {
const newFeature = specSelectors.selectNewFeature()
if (!newFeature || !newFeature.size) {
return null
}
// Render the new feature
return (
<div className="opblock-tag-section">
<h3>New Feature</h3>
{/* Render logic here */}
</div>
)
}
NewFeature.propTypes = {
specSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
}
export default NewFeature
```
### Step 8: Create Component Wrappers
**File:** `src/core/plugins/oas{VERSION_NUMBER}/wrap-components/info.jsx`
**β οΈ Minor version (e.g. 3.2): don't create a new Info component.** If the Info object is identical to the previous version, reuse `OAS{PREV_VERSION}Info` via `getComponent` instead of copying the whole component. Only create a new Info component for major versions with significant structural changes.
**Template (minor version β reuse previous Info):**
```javascript
/**
* @prettier
*/
import React from "react"
import { createOnlyOAS{VERSION}ComponentWrapper } from "../fn.js"
const InfoWrapper = createOnlyOAS{VERSION}ComponentWrapper(({ getSystem }) => {
const system = getSystem()
const OAS{PREV_VERSION}Info = system.getComponent("OAS{PREV_VERSION}Info", true)
return <OAS{PREV_VERSION}Info />
})
export default InfoWrapper
```
**Also β OpenAPIVersion wrapper (minor version β change version badge only):**
For minor versions where the only Info difference is the version badge, use the OpenAPIVersion wrapper pattern instead of wrapping InfoContainer at all:
```javascript
// wrap-components/openapi-version.jsx
/**
* @prettier
*/
import React from "react"
import { createOnlyOAS{VERSION}ComponentWrapper } from "../fn.js"
export default createOnlyOAS{VERSION}ComponentWrapper((props) => {
const { originalComponent: Ori } = props // NOT `Ori` from props directly β use `originalComponent`
return <Ori oasVersion="{MAJOR}.{MINOR}" />
})
```
Then register it in index.js:
```javascript
wrapComponents: {
OpenAPIVersion: OpenAPIVersionWrapper, // changes the version badge shown in the Info header
}
```
### Step 9: Implement afterLoad Hook
**File:** `src/core/plugins/oas{VERSION_NUMBER}/after-load.js`
**Template:**
```javascript
/**
* @prettier
*/
import { wrapOAS{VERSION}Fn } from "./fn.js"
/**
* afterLoad hook for OAS {VERSION} plugin
*
* This hook runs after all plugins are loaded and allows
* modification of the system's functions and behaviors.
*/
function afterLoad({ fn, getSystem }) {
const system = getSystem()
// Override functions that differ in OAS {VERSION}
if (typeof fn.sampleFromSchema === "function") {
// Example: Wrap sample generation if JSON Schema version changed
// const wrappedFns = wrapOAS{VERSION}Fn({
// sampleFromSchema: fn.jsonSchema{VERSION}.sampleFromSchema,
// }, system)
// Object.assign(this.fn, wrappedFns)
}
// Override other functions as needed
// Example: File upload detection
// if (typeof fn.isFileUploadIntended === "function") {
// const wrappedFns = wrapOAS{VERSION}Fn({
// isFileUploadIntended: makeIsFileUploadIntended(system),
// }, system)
// Object.assign(this.fn, wrappedFns)
// }
}
export default afterLoad
```
### Step 10: Handle Authentication Changes
If new authentication types are introduced:
**File:** `src/core/plugins/oas{VERSION_NUMBER}/components/auth/{auth-type}.jsx`
**Template:**
```javascript
/**
* @prettier
*/
import React from "react"
import PropTypes from "prop-types"
const NewAuthType = ({ schema, name, getComponent, onChange, authorized }) => {
const authValue = authorized && authorized.getIn([name])
const Input = getComponent("Input")
const Button = getComponent("Button")
// Render auth UI
return (
<div>
<h4>{schema.get("description") || `${name} (${schema.get("type")})`}</h4>
{/* Auth UI here */}
</div>
)
}
NewAuthType.propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
authorized: PropTypes.object,
}
export default NewAuthType
```
**File:** `src/core/plugins/oas{VERSION_NUMBER}/auth-extensions/wrap-selectors.js`
**Template:**
```javascript
/**
* @prettier
*/
import { createOnlyOAS{VERSION}SelectorWrapper } from "../fn.js"
import { Map } from "immutable"
/**
* Wraps definitionsToAuthorize to include new auth types
*/
export const definitionsToAuthorize = createOnlyOAS{VERSION}SelectorWrapper(
(state, system) =>
(oriSelector) => {
const definitions = oriSelector()
const securityDefinitions = system.specSelectors.securityDefinitions() || Map()
// Add new auth types to definitions
return definitions.map((definition) => {
const schema = securityDefinitions.get(definition.get("name"))
if (schema && schema.get("type") === "newAuthType") {
// Add new auth type logic
return definition.set("newAuthType", true)
}
return definition
})
}
)
```
### Step 11: Register Plugin in Preset
**File:** `src/core/presets/apis/index.js`
**Add import:**
```javascript
import OpenAPI{VERSION}Plugin from "../../plugins/oas{VERSION_NUMBER}/index.js"
```
**Add to preset array (IMPORTANT: add at the end):**
```javascript
export default function PresetApis() {
return [
BasePreset,
OpenAPI30Plugin,
// ... other plugins
OpenAPI{PREVIOUS_VERSION}Plugin,
OpenAPI{VERSION}Plugin, // Load LAST to override previous versions
]
}
```
**Why load last?**
- Wrap-components override previous versions
- Wrap-selectors intercept earlier selectors
- afterLoad modifications apply to dependencies
### Step 12: Add Unit Tests
**Directory:** `test/unit/core/plugins/oas{VERSION_NUMBER}/`
**File:** `test/unit/core/plugins/oas{VERSION_NUMBER}/fn.js`
**Template:**
```javascript
/**
* @prettier
*/
import { fromJS } from "immutable"
import { isOAS{VERSION} } from "src/core/plugins/oas{VERSION_NUMBER}/fn.js"
describe("oas{VERSION_NUMBER} plugin - fn - isOAS{VERSION}", () => {
it("should match OpenAPI {MAJOR}.{MINOR}.0", () => {
const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.0" })
expect(isOAS{VERSION}(spec)).toBe(true)
})
it("should match OpenAPI {MAJOR}.{MINOR}.1", () => {
const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.1" })
expect(isOAS{VERSION}(spec)).toBe(true)
})
it("should match OpenAPI {MAJOR}.{MINOR}.25", () => {
const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.25" })
expect(isOAS{VERSION}(spec)).toBe(true)
})
it("should NOT match OpenAPI {MAJOR}.{MINOR}", () => {
const spec = fromJS({ openapi: "{MAJOR}.{MINOR}" })
expect(isOAS{VERSION}(spec)).toBe(false)
})
it("should NOT match OpenAPI {MAJOR}.{MINOR}.01 (leading zero)", () => {
const spec = fromJS({ openapi: "{MAJOR}.{MINOR}.01" })
expect(isOAS{VERSION}(spec)).toBe(false)
})
it("should NOT match OpenAPI {DIFFERENT_MAJOR}.{DIFFERENT_MINOR}.0", () => {
const spec = fromJS({ openapi: "{DIFFERENT_MAJOR}.{DIFFERENT_MINOR}.0" })
expect(isOAS{VERSION}(spec)).toBe(false)
})
it("should NOT match swagger: 2.0", () => {
const spec = fromJS({ swagger: "2.0" })
expect(isOAS{VERSION}(spec)).toBe(false)
})
it("should handle null spec", () => {
const spec = fromJS({})
expect(isOAS{VERSION}(spec)).toBe(false)
})
})
```
**File:** `test/unit/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx`
**Template:**
```javascript
/**
* @prettier
*/
import React from "react"
import { shallow } from "enzyme"
import OAS{VERSION}VersionPragmaFilter from "src/core/plugins/oas{VERSION_NUMBER}/components/version-pragma-filter.jsx"
describe("OAS{VERSION}VersionPragmaFilter", () => {
it("should render nothing when bypass is true", () => {
const wrapper = shallow(
<OAS{VERSION}VersionPragmaFilter
bypass={true}
isSwagger2={false}
isOAS3={false}
isOAS{PREVIOUS_VERSION}={false}
isOAS{VERSION}={true}
/>
)
expect(wrapper.type()).toBe(null)
})
it("should render nothing when version is valid", () => {
const wrapper = shallow(
<OAS{VERSION}VersionPragmaFilter
bypass={false}
isSwagger2={false}
isOAS3={false}
isOAS{PREVIOUS_VERSION}={false}
isOAS{VERSION}={true}
/>
)
expect(wrapper.type()).toBe(null)
})
it("should render error when version is ambiguous", () => {
const wrapper = shallow(
<OAS{VERSION}VersionPragmaFilter
bypass={false}
isSwagger2={true}
isOAS3={false}
isOAS{PREVIOUS_VERSION}={false}
isOAS{VERSION}={true}
/>
)
expect(wrapper.find(".version-pragma__message--ambiguous")).toHaveLength(1)
})
it("should render error when version is missing", () => {
const wrapper = shallow(
<OAS{VERSION}VersionPragmaFilter
bypass={false}
isSwagger2={false}
isOAS3={false}
isOAS{PREVIOUS_VERSION}={false}
isOAS{VERSION}={false}
/>
)
expect(wrapper.find(".version-pragma__message--missing")).toHaveLength(1)
})
})
```
### Step 13: Add E2E Tests
**Directory:** `test/e2e-cypress/e2e/features/oas{VERSION_NUMBER}/`
**Create test spec file:**
```javascript
/**
* @prettier
*/
describe("OpenAPI {MAJOR}.{MINOR} features", () => {
it("should detect OAS {VERSION} version", () => {
cy.visit("/oas{VERSION_NUMBER}-spec.html")
cy.get(".information-container .version").should("contain", "{MAJOR}.{MINOR}.0")
})
// Add tests for new features
it("should render new feature X", () => {
cy.visit("/oas{VERSION_NUMBER}-spec.html")
cy.get(".opblock-tag-section").should("contain", "New Feature")
})
})
```
**Add test fixtures:**
- Create sample OAS {VERSION} spec in `test/e2e-cypress/static/documents/oas{VERSION_NUMBER}/`
- Create HTML fixture in `test/e2e-cypress/static/` referencing the spec
### Step 14: Update Documentation
**Files to update:**
1. **README.md** - Add OAS {VERSION} to supported versions
2. **CLAUDE.md** - Add plugin to list and update version compatibility
3. **docs/usage/version-detection.md** - Document new version detection
4. **package.json** - Update description if needed
**Example README.md update:**
```markdown
### OpenAPI Specification Compatibility
- **OpenAPI 2.0** (Swagger)
- **OpenAPI 3.0.x**
- **OpenAPI 3.1.x**
- **OpenAPI {MAJOR}.{MINOR}.x** β NEW
```
### Step 15: Verify Build
**Run full build:**
```bash
npm run clean
npm run build
```
**Check dist/ output:**
- `dist/swagger-ui.js` - Should include new plugin
- `dist/swagger-ui.css` - Should include new styles (if any)
- Bundle size should be reasonable
**Run artifact tests:**
```bash
npm run test:artifact
```
### Step 16: Run Full Test Suite
```bash
npm run lint-errors # ESLint errors only
npm run test:unit # Jest unit tests
npm run cy:ci # Cypress E2E tests
```
**All tests must pass before submitting PR.**
### Step 17: Create Pull Request
**Commit message format:**
```
feat(oas{VERSION_NUMBER}): add support for OpenAPI {MAJOR}.{MINOR}
Implement OpenAPI {MAJOR}.{MINOR}.x specification support with:
- Version detection and plugin architecture
- New feature X rendering
- New authentication type Y support
- JSON Schema {VERSION} integration (if applicable)
Closes #{ISSUE_NUMBER}
```
**PR checklist:**
- [ ] All tests passing
- [ ] New feature components implemented
- [ ] Unit tests added
- [ ] E2E tests added
- [ ] Documentation updated
- [ ] Build completes successfully
- [ ] No ESLint errors
- [ ] Follows existing code patterns
## Common Pitfalls