## File: README.md

# 
[](http://badge.fury.io/js/swagger-editor)
[](https://jenkins.swagger.io/job/oss-swagger-editor-master/)
[](https://codeclimate.com/github/swagger-api/swagger-editor)
[](https://jenkins.swagger.io/view/OSS%20-%20JavaScript/job/oss-swagger-editor-master/)

**⏰️ Looking for the next generation version of Swagger Editor?**

Only **SwaggerEditor@5** supports [OpenAPI 3.1.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md). 
SwaggerEditor@4 will not receive OpenAPI 3.1.0 support and is considered deprecated at this point.

---

**🕰️ Looking for the older version of Swagger Editor?** Refer to the [*2.x*](https://github.com/swagger-api/swagger-editor/tree/2.x) or [*3.x*](https://github.com/swagger-api/swagger-editor/tree/3.x) branches.

---

Swagger Editor lets you edit **OpenAPI API definitions** ([OpenAPI 2.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) and [OpenAPI 3.0.3](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md)) 
in JSON or YAML format inside your browser and to preview documentations in real time.
Valid OpenAPI definitions can then be generated and used with the full Swagger tooling (code generation, documentation, etc).

As a brand-new version, written from the ground up, there are some known issues and unimplemented features. Check out the [Known Issues](#known-issues) section for more details.

This repository publishes to two different NPM modules:

* [swagger-editor](https://www.npmjs.com/package/swagger-editor) is a traditional npm module intended for use in single-page applications that are capable of resolving dependencies (via Webpack, Browserify, etc).
* [swagger-editor-dist](https://www.npmjs.com/package/swagger-editor-dist) is a dependency-free module that includes everything you need to serve Swagger Editor in a server-side project, or a web project that can't resolve npm module dependencies.

If you're building a single-page application, using `swagger-editor` is strongly recommended, since `swagger-editor-dist` is significantly larger.

## Anonymized analytics

Swagger Editor uses [Scarf](https://scarf.sh/) to collect [anonymized installation analytics](https://github.com/scarf-sh/scarf-js?tab=readme-ov-file#as-a-user-of-a-package-using-scarf-js-what-information-does-scarf-js-send-about-me). These analytics help support the maintainers of this library and ONLY run during installation. To [opt out](https://github.com/scarf-sh/scarf-js?tab=readme-ov-file#as-a-user-of-a-package-using-scarf-js-how-can-i-opt-out-of-analytics), you can set the `scarfSettings.enabled` field to `false` in your project's `package.json`:

```
// package.json
{
  // ...
  "scarfSettings": {
    "enabled": false
  }
  // ...
}
```

Alternatively, you can set the environment variable `SCARF_ANALYTICS` to `false` as part of the environment that installs your npm packages, e.g., `SCARF_ANALYTICS=false npm install`.

## Helpful scripts

Any of the scripts below can be run by typing `npm run <script name>` in the project's root directory.

### Developing
Script name | Description
--- | ---
`dev` | Spawn a hot-reloading dev server on port 3200.
`deps-check` | Generate a size and licensing report on Swagger Editors's dependencies.
`lint` | Report ESLint style errors and warnings.
`lint-errors` | Report ESLint style errors, without warnings.
`lint-fix` | Attempt to fix style errors automatically.
`watch` | Rebuild the core files in `/dist` when the source code changes. Useful for `npm link`.

### Building
Script name | Description
--- | ---
`build` | Build a new set of JS and CSS assets, and output them to `/dist`.
`build:bundle` | Build `swagger-editor-bundle.js` only (commonJS).
`build:core` | Build `swagger-editor.(js\|css)` only (commonJS).
`build:standalone` | Build `swagger-editor-standalone-preset.js` only (commonJS).
`build:stylesheets` | Build `swagger-editor.css` only.
`build:es:bundle` | Build `swagger-editor-es-bundle.js` only (es2015).
`build:es:bundle:core` | Build `swagger-editor-es-bundle-core.js` only (es2015).

### Testing
Script name | Description
--- | ---
`test` | Run unit tests in Node, run Cypress end-to-end tests, and run ESLint in errors-only mode.
`test:unit-mocha` | Run Mocha-based unit tests in Node.
`test:unit-jest` | Run Jest-based unit tests in Node.
`e2e` | Run end-to-end browser tests with Cypress.
`lint` | Run ESLint test
`test:artifact` | Run list of bundle artifact tests in Jest
`test:artifact:umd:bundle` | Run unit test that confirms `swagger-editor-bundle` exports as a Function
`test:artifact:es:bundle` | Run unit test that confirms `swagger-editor-es-bundle` exports as a Function
`test:artifact:es:bundle:core` | Run unit test that confirms `swagger-editor-es-bundle-core` exports as a Function


## Running locally

### Prerequisites

- git, any version
- **Node.js >=20.3.0** and **npm >=9.6.7** are the minimum required versions that this repo runs on, but we always recommend using the latest version of Node.js.

```shell
 $ npm i --legacy-peer-deps
```

If you have Node.js and npm installed, you can run `npm start` to spin up a static server.

Otherwise, you can open `index.html` directly from your filesystem in your browser.

If you'd like to make code changes to Swagger Editor, you can start up a Webpack hot-reloading dev server via `npm run dev`.

##### Browser support

Swagger Editor works in the latest versions of Chrome, Safari, Firefox, and Edge.

### Known Issues

To help with the migration, here are the currently known issues with 3.X. This list will update regularly, and will not include features that were not implemented in previous versions.

- Everything listed in [Swagger UI's Known Issues](https://github.com/swagger-api/swagger-ui/blob/master/README.md#known-issues).
- The integration with the codegen is still missing.

## Docker

### Running the image from DockerHub

There is a docker image published in **docker.swagger.io** registry.

To use this, run the following:

```
docker pull docker.swagger.io/swaggerapi/swagger-editor
docker run -d -p 80:8080 docker.swagger.io/swaggerapi/swagger-editor
```

This will run Swagger Editor (in detached mode) on port 80 on your machine, so you can open it by navigating to `http://localhost` in your browser.  

* You can provide a URL pointing to an API definition (may not be available if some security policies such as CSP or CORS are enforced):

```
docker run -d -p 80:8080 -e URL="https://petstore3.swagger.io/api/v3/openapi.json" docker.swagger.io/swaggerapi/swagger-editor
```

* You can provide your own `json` or `yaml` definition file from your local host:

```
docker run -d -p 80:8080 -v $(pwd):/tmp -e SWAGGER_FILE=/tmp/swagger.json docker.swagger.io/swaggerapi/swagger-editor
```

**Note:** When both `URL` and `SWAGGER_FILE` environment variables are set, `URL` has priority and `SWAGGER_FILE` is ignored.

* You can specify a different base url via `BASE_URL` variable for accessing the application - for example if you want the application to be available at `http://localhost/swagger-editor/`:

```
docker run -d -p 80:8080 -e BASE_URL=/swagger-editor docker.swagger.io/swaggerapi/swagger-editor
```

* You can specify a different port via `PORT` variable for accessing the application, default is `8080`.

```
docker run -d -p 80:80 -e PORT=80 docker.swagger.io/swaggerapi/swagger-editor
```

* You can specify Google Tag Manager ID via `GTM` variable for tracking the usage of the swagger-editor.

```
docker run -d -p 80:8080 -e GTM=GTM-XXXXXX docker.swagger.io/swaggerapi/swagger-editor
```

You can also customize the different endpoints used by the Swagger Editor with the following environment variables. For instance, this can be useful if you have your own Swagger generator server:

Environment variable | Default value
--- | ---
`URL_SWAGGER2_GENERATOR` | `https://generator.swagger.io/api/swagger.json`
`URL_OAS3_GENERATOR` | `https://generator3.swagger.io/openapi.json`
`URL_SWAGGER2_CONVERTER` | `https://converter.swagger.io/api/convert`

If you want to run the Swagger Editor locally without the Codegen features (Generate Server and Generate Client) you can set the above environment variables to `null` (`URL_SWAGGER2_CONVERTER=null`).

### Building and running an image locally

To build and run a docker image with the code checked out on your machine, run the following from the root directory of the project:

```
# Install npm packages (if needed)
npm install

# Build the app
npm run build

# Build an image
docker build -t swagger-editor .

# Run the container
docker run -d -p 80:8080 swagger-editor
```

You can then view the app by navigating to `http://localhost` in your browser.

## Documentation

* [Importing your OpenAPI document](docs/import.md)

* [Contributing](https://github.com/swagger-api/.github/blob/master/CONTRIBUTING.md)

### Using older version of React

> [!IMPORTANT]
> By older versions we specifically refer to `React >=17 <18`.

By default [swagger-editor@4](https://www.npmjs.com/package/swagger-editor) npm package comes with latest version of [React@18](https://react.dev/blog/2022/03/29/react-v18).
It's possible to use _swagger-editor@4_ npm package with older version of React.

Let's say my application integrates with _swagger-editor@4_ npm package and uses [React@17.0.2](https://www.npmjs.com/package/react/v/17.0.2).

### npm

In order to inform `swagger-editor@4` npm package that I require it to use my React version, I need to use [npm overrides](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides).

```json
{
  "dependencies": {
    "react": "=17.0.2",
    "react-dom": "=17.0.2"
  },
  "overrides": {
    "swagger-editor": {
      "react": "$react",
      "react": "$react-dom",
      "react-redux": "^8"
    }
  }
}
```

> [!NOTE]
> The React and ReactDOM override are defined as a reference to the dependency. Since _react-redux@9_ only supports `React >= 18`, we need to use _react-redux@8_.


### yarn

In order to inform `swagger-editor@4` npm package that I require it to use my specific React version, I need to use [yarn resolutions](https://yarnpkg.com/cli/set/resolution).


```json
{
  "dependencies": {
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "resolutions": {
    "swagger-editor/react": "17.0.2",
    "swagger-editor/react-dom": "17.0.2",
    "swagger-editor/react-redux": "^8"
  }
}
```

> [!NOTE]
> The React and ReactDOM resolution cannot be defined as a reference to the dependency. Unfortunately *yarn* does not support aliasing like `$react` or `$react-dom` as *npm* does. You'll need to specify the exact versions.

## Security contact

Please disclose any security-related issues or vulnerabilities by emailing [security@swagger.io](mailto:security@swagger.io), instead of using the public issue tracker.

---

## File: docs/customization/plug-points/editor-monaco-language-apidom.md

# editor-monaco plug points

## Configuring web worker capabilities

Worker accepts configuration for a language service via ApiDOM Context configuration object.
By default, this configuration looks like this:

```js
{
  validatorProviders: [],
  completionProviders: [],
  performanceLogs: false,
  logLevel: apidomLS.LogLevel.WARN,
  completionContext: {
    maxNumberOfItems: 100,
    enableLSPFilter: false,
  },
}
```

If you want to override the default ApiDOM Context configuration object, you need to pass `apiDOMContext` option to the `EditorMonacoLanguageApiDOM` plugin.

```js
EditorMonacoLanguageApiDOM({
  createData: {
    apiDOMContext: {
      completionContext: {
        enableLSPFilter: true, // enables "strict" word filtering (instead of default Monaco fuzzy matching; https://github.com/swagger-api/apidom/pull/2954)
      },
    },
  },
});
```

> NOTE: note that the provided ApiDOM Context configuration object is merged with default ApiDOM Context configuration object
using [deep-extend](https://www.npmjs.com/package/deep-extend) npm package.

## Extending web worker capabilities

`editor-monaco-language-apidom` comes with implementation of `apidom` language.
The plugin comes with `apidom.worker` utilizing ApiDOM capabilities.
`apidom.worker` can be extended in two ways: dynamic and static.

### Dynamic extension

Dynamic extension happens during runtime, and we recommend to use it only for simple use-cases.

First thing we need to do is to pass a `customApiDOMWorkerPath` option to the `EditorMonacoLanguageApiDOM` plugin.

```js
EditorMonacoLanguageApiDOM({
  createData: {
    customApiDOMWorkerPath: 'https://example.com/index.js',
  },
});
```

`customApiDOMWorkerPath` is a URL (absolute or relative) of the extending module. The `apidom.worker`
loads it via dynamic `import()` at runtime.

The module must export a `customApiDOMWorkerFactory` function:

**https://example.com/index.js**

```js
export const customApiDOMWorkerFactory = (ApiDOMWorkerClass, toolbelt) => {
  return ApiDOMWorkerClass;
};
```

The function receives two arguments:

- `ApiDOMWorkerClass` - the class that implements the editor capabilities
- `toolbelt` - an object containing various library exports

Here is a simple **example** demonstrating changing the log level of language service:

```js
export const customApiDOMWorkerFactory = (ApiDOMWorkerClass, toolbelt) => {
  const { apidomLS } = toolbelt;

  class ApiDOMWorkerLogLevelErrorClass extends ApiDOMWorkerClass {
    static apiDOMContext = {
      ...ApiDOMWorkerClass.apiDOMContext,
      logLevel: apidomLS.LogLevel.ERROR,
    };
  }

  return ApiDOMWorkerLogLevelErrorClass;
};
```

> **Note:** Classic (non-module) worker consumers may still use the `globalThis.customApiDOMWorkerFactory`
> assignment pattern — it is supported as a fallback when a named export is absent.

### Static extension

Static extension requires a bundler (Vite, webpack, etc.) to build a custom worker entry point.

**my-custom-apidom.worker.js**

```js
import { initialize, makeCreate, ApiDOMWorker } from 'swagger-editor/apidom.worker';

class ApiDOMWorkerExtended extends ApiDOMWorker {
  // implementation of extensions
}

const create = makeCreate(ApiDOMWorkerExtended);

initialize((ctx, createData) => create(ctx, createData));

export { initialize, create, makeCreate, ApiDOMWorkerExtended as ApiDOMWorker };
```

Point your bundler at `my-custom-apidom.worker.js` as the `apidom.worker` entry and configure
`MonacoEnvironment.getWorker` to serve the built file. For example, with webpack:

```js
  entry: {
    app: './index.js',
    'apidom.worker': './my-custom-apidom.worker.js',
    'editor.worker': 'swagger-editor/editor.worker',
  }
```

## Passing data to web workers

Often when extending web worker capabilities it is the case that we need to pass additional
data to web worker. These data may include any arbitrary data compatible with
[the structured clone algorithm](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).

Let's consider the following use-case. We need to extend the `apidom.worker` in a way that
it will be fetching data on demand from authorized REST endpoint.

### Dynamic extension

`EditorMonacoLanguageApiDOM` plugin configuration.

```js
EditorMonacoLanguageApiDOM({
  createData: {
    authToken: 'c32d8b45-92fe-44f6-8b61-42c2107dfe87',
    customApiDOMWorkerPath: 'https://example.com/index.js',
  },
});
```

**https://example.com/index.js**

```js
export const customApiDOMWorkerFactory = (ApiDOMWorkerClass, toolbelt) => {
  const { apidomLS } = toolbelt;

  class ApiDOMWorkerLogLevelErrorClass extends ApiDOMWorkerClass {
    static apiDOMContext = {
      ...ApiDOMWorkerClass.apiDOMContext,
      logLevel: apidomLS.LogLevel.ERROR,
    };

    async loadData() {
      // createData passed as plugin option is available in worker as this._createData
      const { authToken } = this._createData;

      return await fetch(`https://example.com/data?authToken=${authToken}`)
    }
  }

  return ApiDOMWorkerLogLevelErrorClass;
};
```

### Static extension

Whenever you extend the `ApiDOMWorker` class you will have `_createData` public property available.

**my-custom-apidom.worker.js**

```js
import { initialize, makeCreate, ApiDOMWorker } from 'swagger-editor/apidom.worker';

class ApiDOMWorkerExtended extends ApiDOMWorker {
  async loadData() {
    // createData passed as plugin option is available in worker as this._createData
    const { authToken } = this._createData;

    return await fetch(`https://example.com/data?authToken=${authToken}`)
  }
}

const create = makeCreate(ApiDOMWorkerExtended);

initialize((ctx, createData) => create(ctx, createData));

export { initialize, create, makeCreate, ApiDOMWorkerExtended as ApiDOMWorker };
```

---

## File: docs/customization/plug-points/README.md

# Plug points

- [editor-monaco-language-apidom](./editor-monaco-language-apidom.md)

---

## File: docs/architecture.md

# High level architecture of SwaggerEditor@5

This document briefly describes high level architecture of SwaggerEditor@5.

## Plugin architecture

SwaggerEditor@5 is composed of number of plugins. These plugins are divided into four
distinct categories

1. Plugins providing editor implementations
2. Plugins providing preview of editor content
3. Editor implementation support plugins
4. Generic features plugins

### Plugins providing editor implementations

These plugins include `editor-textarea` and `editor-moanco`.

`editor-textarea` is a base plugin that other editor implementations build on.
This plugin provides basic editing experience using `<textarea />` HTML tag.

`editor-monaco` builds on top of `editor-textarea` and provide advanced editing
experience using [Monaco Editor](https://microsoft.github.io/monaco-editor/).

### Plugins providing preview of editor content

These plugins include `editor-preview-swagger-ui`, `editor-preview-asyncapi` and others.
The single responsibility of editor preview plugin is to render the editor content (text)
to a set of UI components.

### Editor implementation support plugins

These plugins include `editor-persistence`, `editor-read-only` and others.
Plugins in this category are responsible to support plugins providing editor implementation
with additional enhanced capabilities in generic way.

### Generic features plugins

These plugins include `dialogs`, `modals`, `layout` and others. Each plugin in this
category provide features or enhancements to plugins in previous three categories.

## Future architectural changes

This section describes opportunities to make the architecture of SwaggerEditor@5 better.

### State management

Currently, editor content (text) is stored in SwaggerUI `spec` plugin. This causes various issues
which manifests in decreased user experience of editing text. Every time the editor content
changes the `spec` plugin will try to parse it as JSON or YAML, resolve it and store it in
redux state. This is absolutely not necessary. We can store editor content in editor implementation plugin
and have preview plugins work as finite state machines and detect if they should process the editor content in any way.
When this architectural change is implemented, the typing lag on big amount of editor content (text)
will significantly decrease.

---

## File: docs/migration.md

# Migrate from SwaggerEditor@4 to SwaggerEditor@5

## Table of Contents
- [Introduction](#introduction)
- [Development](#development)
  - [Start script with hot-reloading](#start-script-with-hot-reloading)
  - [Static build script](#static-build-script)
  - [Local & remote definitions](#local--remote-definitions)
  - [Linting](#linting)
  - [Dependencies](#dependencies)
  - [Testing](#testing)
- [Design](#design)
  - [Plugins](#plugins)
    - [plugins available at release](#plugins-available-at-release)
    - [plugins not yet migrated](#plugins-not-yet-migrated)
  - [Layout](#layout)


### Introduction
SwaggerEditor@5 is a ground-up rewrite of SwaggerEditor@4. However, SwaggerEditor@5 is still fundamentally based on the SwaggerUI plugin system, and continues to be developed within the React ecosystem. For users without custom plugins or other forked customizations, SwaggerEditor@5 is intended to be mostly drop-in compatible with the existing SwaggerEditor@4.

SwaggerEditor@5 introduces integration with ApiDOM and Microsoft's Monaco Editor. ApiDOM replaces the parser from SwaggerClient, as well as existing custom validation from SwaggerEditor@4. Monaco Editor replaces Ace Editor. Additional documentation on features and usage of [ApiDOM](https://github.com/swagger-api/apidom) and [Monaco Editor](https://github.com/microsoft/monaco-editor) can be found within their respective documention. Also available are the [ApiDOM Playground](https://swagger-api.github.io/apidom/) and [Monaco Editor Playground](https://microsoft.github.io/monaco-editor/playground.html)

This migration guide is intended to highlight setup changes and key customization features for SwaggerEditor@5.


### Development
SwaggerEditor@5 is using Create React App as it's building infrastructure. Therefore there are a few minor differences
to develop SwaggerEditor@5
#### Start script with hot-reloading
new:
```
$ npm start
```

old:
```
$ npm run dev
```

#### Static build script
new:
```
$ npm run build:app
$ npm run build:app:serve
```

old:
```
$ npm start
```

#### Local & remote definitions
Only files inside `public` can be used from `public/index.html`. JS files must be put inside `src`.

new:
```
<SwaggerEditor url={url} />
```

old:
In `dev-helpers/index.html`, add a `url` key like this:
```
const editor = SwaggerEditorBundle({
  dom_id: '#swagger-editor',
  url: "some-local-or-remote-path-to-definition.yaml" # define path here
  layout: 'StandaloneLayout',
  presets: [
    SwaggerEditorStandalonePreset
  ]
})
```


#### Linting
Automatic linting checks remain part of the commit process. SwaggerEditor@5 now generally follows the eslint recommendations from the AirBnb and React teams.

#### Dependencies

SwaggerEditor@5 is now based on `swagger-ui-react` instead of `swagger-ui`. In addition, unlike SwaggerEditor@4, SwaggerEditor@5 does not have a dependency on `SwaggerClient`.


#### Testing

Unit tests are now covered by `jest` and `@testing-library`. Unit tests are now co-located next to their component. Component tests follow the `@testing-library` philosophy to avoid including implementation details, so that changes to implementation but not functionality doesn't break existing tests.

E2E tests are now covered by Playwright and are located in the `test/playwright` directory.


### Design

#### Plugins

SwaggerEditor@5 maintains its core as an extension of SwaggerUI's plugin system. SwaggerEditor@5 exports itself as a fully realized set of plugins, each of which can be extended and wrapped as needed. This even includes the new Monaco Editor feature as a plugin!

Compared to SwaggerEditor@4, there is also no change to precedence with regards to the order of loading plugins.

SwaggerEditor@5 provides a `modals` plugin for a unified modal system. The `modals` plugin is further extended via the `dialogs` plugin which provides a set of `alert` and `confirm` modal dialogs.

Any existing custom plugins for SwaggerEditor@4 that modified the behavior of, or directly interfaced with, the Ace Editor will likely need to be heavily refactored. Custom validation rules should be migrated to ApiDOM as needed.


##### plugins available at release
- `dialogs`
- `dropzone`
- `editor-monaco`
- `editor-persistence`
- `editor-preview-asyncapi`
- `editor-preview-swagger-ui`
- `editor-read-only`
- `editor-spec-origin`
- `editor-textarea`
- `layout`
- `modals`
- `topbar`

##### plugins not yet migrated

At the time of this writing there are two plugins from SwaggerEditor@4 that have not been migrated:
1. `jump-to-path`. Note: expect to leverage ApiDOM and `editor-monaco` plugin to replace AST and Ace Editor.
2. `topbar-insert`. Note: expect to leverage and wrap components of various existing plugins: `topbar`, `editor-monaco`, `modals`, and `dialogs`.



#### Layout

SwaggerEditor@5 comes with a plugin for a "core" layout. The "core" layout provides plug points that are analogous to "panes" and/or "bars" in other IDEs. These plug points make it easier for developers to create plugins that customize their UX with as minimal changes to the "core" layout as possible.

The "core" layout includes contains `EditorPane`, `EditorPreviewPane`, and `Topbar`. These three components represent the basic UX wireframe for SwaggerEditor@5. In addition, the `EditorPane` is provided with four surrounding `bars` (top, bottom, left, right) that can each be customized with their own wrapped implementations.

Using itself as a reference design, SwaggerEditor@5 provides implementation of two different `EditorPane` (`editor-monaco` and `editor-textarea`), and two different `EditorPreviewPane` (`editor-preview-swagger-ui` and `editor-preview-asyncapi). The `editor-monaco` plugin also further extends the base `EditorPaneBarTop` and `EditorPaneBarBottom` with its own wrapped version of each.

---

## File: docs/migration-legacy-summary.md

## Swagger Editor Legacy (v3/v4) Migration Summary

### Topbar Features
[x] Import from URL  
[x] Import from File  
[x] Save As Json  
[x] Save As Yaml  
[x] Convert from Json to Yaml, then download  
[x] Convert from Yaml to Json, then download  
[x] Convert from Json to Yaml, in editor only  
[x] Clear Editor (moved from File Menu to Edit Menu)  
[x] Convert from OAS2 to OAS3  
[x] Generate Servers list - OAS2  
[x] Generate Servers list - OAS3.0  
[x] Generate Clients list - OAS2  
[x] Generate Clients list - OAS3.0  
[x] Display/Hide menu items based on combination of json/yaml, oas2/oas3  
[x] NEW: consistent use of styled Modal components throughout (react-modal)  
[x] NEW: Load default OAS2/OAS3/OAS3.1/AsyncApi2 definition; w/keyboard shortcuts  
[x] Removed: topbar-insert (plugin); no replacement planned  


### Editor Features
[x] NEW: Monaco Editor  
[x] NEW: validation from apidom-ls library
[x] Removed: Ace Editor  
[x] Removed: internal validation from swagger-editor v3/v4
[x] Drag-n-drop local file to editor (react-dropzone)  
[x] Persist definition on browser refresh/reload (localStorage)  
[x] Display validation pane results


### Visual UI Features
[x] SwaggerUI v4; React 17  


### Implementation Changes
[x] React Hooks support, e.g. menu item display toggle, modal system  
[x] Behavioral testing of components, e.g. not the implementation details  
[x] Simplify and reorganize `presets`, `layouts`, and `plugins`  
[x] Extract menu action methods from Components to Actions  
[x] Extract menu logic helpers to Actions  
[x] Extract shared menu logic helpers to `utils/editor-converter`  
[x] Replace `fetch` with `axios` http helpers to `utils/topbar-http`  
[x] deprecate plugins with direct implementation: Convert to OAS3, Import File  
[x] Removed: unnecessary state where possible  
[x] Removed: window.alert and window.confirm popups
[x] Removed: swagger-client library dependency  
[x] Tests: testing-library + jest (unit), playwright (e2e)  


### Possible additional/expanded docs/contributing topics  
[x] Separation of actions from components. tldr: easier to test, possibly more re-usable
[x] React Hooks. tldr: modern practices  
[x] testing-library. tldr: focus on user behaviors, more durable/maintainable tests  
[x] reorganizing the presets. e.g. what happened to `standalone` and other plugins? The new recommended structure. tldr: multi-spec, multi-layouts  


### List of migrated legacy methods  
Note: likely to remove this section from final doc  

Extract menu action methods from React to Actions  
[x] importFromURL  
[x] saveAsYaml  
[x] saveAsJson  
[x] convertToYaml  
[x] downloadGeneratedFile  
[x] importFile  
[x] onDocumentLoad prop - removed. handled now in actions. Theoretically, we could expose as a user-overwritable function. Maybe SH needed it?  
[x] updateEditorContent prop - will be removed, and handled in actions.  
[x] clearEditor  

Deprecate methods from React  
[x] saveAsText  
[x] handleResponse  

Extract menu logic helpers to Actions
[x] getGeneratorUrl  
[x] instantiateGeneratorClient  
[x] shouldReInstantiateGeneratorClient (new)  

Extract menu logic helpers to utils-converter
[x] hasParserErrors  
[x] getFileName  
[x] getDefinitionLanguage  
[x] getDefinitionVersion  

Create (axios) http method to utils-http
[x] getDefinitionFromUrl  
[x] getGenerator2Definition  
[x] postPerformOasConversion  

Remove unnecessary state:
[x] swaggerClient  
[x] definitionVersion  

Remove use of alert and confirm via new modal system
[x] alert  
[x] confirm  


### Additional notes (remove from final doc)  
[x] swagger-ui redux state  
[x] connect monaco state to swagger-ui redux state  
[x] connect actions with monaco state, e.g. updateEditorContent  
[x] init oas spec via swagger-ui  
[x] connect & sync generic-editor updates to swagger-ui  
[x] init oas spec via import File (finish action) - Json  
[x] init oas spec via import File (finish action) - Yaml  
[x] init oas spec via import Url (finish action)  
[x] CSS/Less/Saas styling  
[x] remove use of mock data in topbarActions. (mock fixtures not removed yet)  
[x] try react-modal lib instead of creating internal version  
[x] modify generic-editor plugin to have a default editor placeholder (instead of calling GenericEditorContainer directly)  
[x] add disposables array, and ability to dispose()  
[x] make generator servers/clients toggleable; enable oas3.1/others when supported  
[x] File Menu dropdown - add json/yaml detection to display appropriate link  
[x] Edit menu dropdown - render convert to OAS3 only if currently 'isSwagger2`  
[x] Edit menu dropdown - render convert to Yaml only if currently 'json'  
[x] onboard - default definition should be in YAML format  

### Resolved Issues (remove from final doc)  

[x] ~~fix exploding styling/rendering when using in-browser (ctrl+f) find text within monaco; monaco's find+replace feature~~  USING P2M/M2P FIXES THIS ISSUE  
[x] ~~warning: overlapping semantic tokens~~ THIS IS APIDOM-LS ISSUE  
[x] fix editor configuration/onChange to always word-wrap.  
[x] remove "dev mode" case when editor content is undefined  
[x] remove mock data/fixtures/configuration from `actions.js`  
[x] in codeActionUi, sometimes recieve an Uncaught promise TypeError: d.dispose is not a funtion  
[x] asyncapi support: anywhere we should be detecting async api (or any other supported spec); e.g. clearEditor  
[x] handle apidom parser throw, when unable to detect langugage. e.g. empty string, or oas2  
[x] load default (oas3). note the topbar generate server/client exists. -> clear all. user may type random string. note that the topbar generate server/client did NOT disappear  
[x] load default -> import OAS2. note the topbar generate server/client exists. -> clear all. user may type random string. note that the topbar generate server/client did NOT disappear

---

## File: docs/migration-tasks-todo.md

# Migration Status  

* [Swagger Editor Legacy (v3/v4) Migration Summary](./migration-legacy-summary.md)  

### Known Issues  

[ ] handle case(s) when specSelectors.method returns undefined  

### Integration Tasks  
[ ] match and extend configurability options for editor & monaco  

### Backlog
[ ] update "minimal" specs/fixtures for `clearEditor`; oas2, oas3, oas3_1, asycapi2  
[ ] memory performance benchmarks  
[ ] if refresh empty monaco, ~~should~~ MAY load a default definition  
[ ] jump-to-path (plugin)  


### Ideas
[ ] topbar: topbar-insert (plugin); this should be integrated into the `Topbar` plugin  
[ ] topbar: update generator (plugin?) for oas3.1, asyncapi, etc. support when available  
[ ] monaco: update syntax highlighting as needed  
[ ] monaco - additional initial style and configuration of monaco editor, as needed  


### Test Coverage Ideas   
[ ] should add unit tests with bad/invalid urls; generator, import Url  
[ ] should add unit tests when both swagger2 and oas3 flags set to same value (both true, both false)  
[ ] should add unit tests allowing exclusion of both swagger2 and oas3, e.g. future asynapi, graphql, etc.  
[ ] e2e: monaco-editor features

---

## File: docs/monaco-worker.md

Monaco Client (React)
  - setup a Monaco instance
    - setup monaco environment use of language worker(s) to use
    - instantiate web workers
    - register providers via Adapters
  - create a Monaco instance
Adapter
  - an adapter for each service or provider
  - generally expect a `provide[Description]` method that matches monaco interface
  - offloads language service functions to language web worker
  - run and/or return a monaco editor "effect". e.g. hover, completion, based on web worker result
  - not required to use Typescript
WebWorker
  - called via an adapter
  - call a language service method in separate thread
  - recommended use by Monaco
LanguageService
  - called via a web worker
  - any lib, in our case, apidom
  - examples include typescript/javascript, json, css


notes:
- some minor naming differences across libs

---

## File: docs/swagger-client.md

Swagger-client is used as middleware to transform `generator` responses,
which also includes function methods alongside data objects.

fetch GET call to `SwaggerClient(url, [options])` will return `response.apis` methods.
- reminder to attach `.catch()` to any `response.apis.[method]` call
```
response.apis.clients:
- clientOptions: ƒ(parameters)
- downloadFile: ƒ(parameters)
- generateClient: ƒ(parameters)
- getClientOptions: ƒ(parameters)

response.apis.servers:
- downloadFile: ƒ(parameters)
- generateServerForLanguage: ƒ(parameters)
- getServerOptions: ƒ(parameters)
- serverOptions: ƒ(parameters)
```

### Summary
In order to download a generated definition, `legacy swagger-editor` made two calls to swagger-client.

1. Given OAS3 or Swagger2/OAS2 definition, swagger-editor makes a request to Generator3 or Generator2 respectively.

Generator3/Generator2 returns a response that includes it's own definition, which `swagger-client`, as middleware, translates the defintion included in Generator's response into a more usable `response.apis.clients` format. This format also includes new `execute` methods for `swagger-editor` to call.

2. `swagger-editor` calls the appropriate `execute` method that was created by `swagger-client` to download the generated definition. Note, Generator3 returns the Blob directly, while Generator2 returns a json object containing an URI link to the Blob.

Additionally, `legacy swagger-editor` saved these methods to React state for general purpose use, e.g. downloadFile vs clientOptions.

In this new version of swagger-editor, use of `swagger-client` has been deprecated, so these methods are no longer available or saved. Equivalent functionality to directly interface with Generator3/Generator2 now exists in `topbarActions`. This change also should now always yield a deterministic final result.

---

## File: docs/wasm-bundling.md

# WASM Bundling in ESM/UMD Library Builds

The `apidom.worker.js` bundle contains three WASM binaries that must be fully
self-contained — no runtime HTTP fetches — for the worker to function inside a
Webpack-bundled consumer (swagger-editor-plus and similar).

All three WASMs are handled by the `inlineAllWasms` Vite plugin defined in
`vite.config.shared.js` and used by both `vite.config.esm.js` and `vite.config.umd.js`.
See the [Plugin ordering](#plugin-ordering) section for how the three hooks work together.

## The three binaries

| Binary | Source | How it is loaded at runtime |
|--------|--------|-----------------------------|
| `tree-sitter.wasm` | `web-tree-sitter` (emscripten) | Emscripten's own `locateFile()` / `fetch()` machinery |
| `tree-sitter-yaml.wasm` | `@swagger-api/apidom-parser-adapter-yaml-1-2` | `web-tree-sitter` `Language.load(arg)` |
| `tree-sitter-json.wasm` | `@swagger-api/apidom-parser-adapter-json` | `web-tree-sitter` `Language.load(arg)` |

---

## `tree-sitter.wasm` — emscripten runtime binary

### The problem

Naively inlining `tree-sitter.wasm` as a data URI (e.g. by replacing the
`'tree-sitter.wasm'` string literal in the bundle) breaks emscripten's fetch
interception. Tools like `@rollup/plugin-wasm` patch `globalThis.fetch` with an
`endsWith('tree-sitter.wasm')` guard:

```js
globalThis.fetch = (...args) =>
  isString(args[0]) && args[0].endsWith('tree-sitter.wasm')
    ? realFetch(tree_sitter_default, ...)
    : realFetch(...args);
```

If the literal is replaced with a ~250 KB data URI, the `endsWith` condition
becomes `url.endsWith('<250KB data URI>')`. A string always ends with itself, so
when emscripten then calls `fetch(dataUri)`, the broken branch fires and passes
the loader *function* as the URL argument to `realFetch`, producing a request to
`http://…/function%20tree_sitter_default(Sn)%20{…}`.

### The fix — `renderChunk` hook in `inlineAllWasms`

Instead of replacing the `'tree-sitter.wasm'` literal, inject the binary into
emscripten's `Module` object **before** any fetch is attempted. Emscripten reads
`Module.wasmBinary` into a local variable during module init, and
`getBinaryPromise()` checks that variable first:

```js
function getBinaryPromise(path) {
  return wasmBinary          // ← truthy → fetch is never called
    ? Promise.resolve().then(() => getBinarySync(path))
    : readAsync(path)...;
}
```

The `renderChunk` hook in `inlineAllWasms` matches the emscripten module
init line (present in Rollup's output before esbuild minifies it):

```
var Module = typeof Module != "undefined" ? Module : {};
```

and injects `Module['wasmBinary'] = new Uint8Array(atob("…").split("").map(…))`
immediately after it. By the time emscripten reaches `getBinaryPromise`, the
local `wasmBinary` is already set and `fetch()` is never called.

---

## Grammar WASMs — `Language.load()` binaries

### The problem

The apidom yaml/json adapter packages import their grammar WASMs as ES modules:

```js
// @swagger-api/apidom-parser-adapter-yaml-1-2/src/lexical-analysis/browser.mjs
import treeSitterYaml from '../../wasm/tree-sitter-yaml.wasm';
// ...
await Language.load(treeSitterYaml);
```

Without intervention, a WASM plugin such as `@rollup/plugin-wasm` would convert
each `.wasm` import into an async loader function:

```js
function tree_sitter_yaml_default(imports) {
  return _loadWasmModule(0, null, ``, imports);
}
```

`Language.load()` in `web-tree-sitter` accepts either a **URL string** or a
**`Uint8Array`**. It does NOT accept a function. When given a function it calls
`fetch(fn)`, which stringifies the function and tries to fetch:

```
http://…/function%20tree_sitter_yaml_default(Sn)%20{…}
```

### Why a plain `load` hook does not work (historical context)

`@rollup/plugin-wasm` has both a `load` hook and a `transform` hook:

```js
// @rollup/plugin-wasm — simplified
load(id) {
  if (!/\.wasm$/.test(id)) return null;
  // reads file, emits asset
},
transform(code, id) {
  if (code && /\.wasm$/.test(id)) {
    // replaces module content with the loader function code
  }
}
```

If a preceding plugin's `load` hook returns valid JavaScript for a `.wasm` id,
`@rollup/plugin-wasm`'s `transform` hook still fires (the id still ends with
`.wasm`) and overwrites the JS with the loader function. The fix must prevent
the `transform` hook from seeing the `.wasm` id at all.

<a id="plugin-ordering"></a>
### The fix — `inlineAllWasms` in `vite.config.shared.js`

All three WASMs are handled by a single `inlineAllWasms` Vite plugin (shared
between `vite.config.esm.js` and `vite.config.umd.js` via `vite.config.shared.js`).
It combines three hooks:

```js
const inlineAllWasms = () => {
  const treeSitterBase64 = readFileSync('node_modules/web-tree-sitter/tree-sitter.wasm').toString('base64');
  return {
    name: 'inline-all-wasms',
    enforce: 'pre',

    // Redirect every .wasm import to a virtual id that does NOT end with '.wasm',
    // bypassing any downstream plugin that checks id.endsWith('.wasm').
    async resolveId(id, importer) {
      if (!id.endsWith('.wasm')) return null;
      const resolved = await this.resolve(id, importer, { skipSelf: true });
      if (!resolved) return null;
      return '\0wasm-inline:' + resolved.id + ':inline';
    },

    // Export the raw bytes as a Uint8Array. Language.load(bytes) accepts this directly.
    // For tree-sitter.wasm the exported value is unused — emscripten reads Module.wasmBinary.
    load(id) {
      if (!id.startsWith('\0wasm-inline:')) return null;
      const filePath = id.slice('\0wasm-inline:'.length, -':inline'.length);
      const base64 = readFileSync(filePath).toString('base64');
      return `const bytes=new Uint8Array(atob("${base64}").split("").map(function(c){return c.charCodeAt(0)}));export default bytes;`;
    },

    // Inject Module['wasmBinary'] into the final bundle at emscripten's module
    // init line so getBinaryPromise() short-circuits before fetch() is called.
    renderChunk(code) {
      const updated = code.replace(
        /var Module\s*=\s*typeof Module\s*!=\s*["']undefined["']\s*\?\s*Module\s*:\s*\{\}/,
        (match) => `${match};Module['wasmBinary']=new Uint8Array(atob("${treeSitterBase64}").split("").map(function(c){return c.charCodeAt(0)}))`
      );
      return updated !== code ? { code: updated } : null;
    },
  };
};
```

`inlineAllWasms` must be a **Vite plugin** (in the `plugins` array), not a
Rollup plugin (in `rollupOptions.plugins`). Rollup plugins run after Vite's
transform pipeline, so placing it in `rollupOptions.plugins` would not give it
priority over other WASM-handling plugins.

---

## Verification after a build

```sh
# Check tree-sitter.wasm is inlined (Module.wasmBinary injection present)
grep -c "wasmBinary" dist/esm/apidom.worker.js   # → ≥1
grep -c "wasmBinary" dist/umd/apidom.worker.js   # → ≥1

# Check grammar WASMs are Uint8Arrays, not loader functions
grep -c "tree_sitter_yaml_default\|tree_sitter_json_default" dist/esm/apidom.worker.js  # → 0
grep -c "tree_sitter_yaml_default\|tree_sitter_json_default" dist/umd/apidom.worker.js  # → 0

# Language.load should receive bytes$N (Uint8Array), not a function
grep "Language.load(" dist/esm/apidom.worker.js  # → Language.load(bytes$1)
```