npm package: @speclynx/apidom-reference
Description: Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.
Author: Vladimir Gorej
Latest version: 4.3.1

# @speclynx/apidom-reference

`@speclynx/apidom-reference` package contains advanced algorithms for semantic ApiDOM manipulations.
This package is divided into three (3) main components:

- **[Parse component](#parse-component)**
- **[Resolve component](#resolve-component)**
- **[Dereference component](#dereference-component)**
- **[Bundle component](#bundle-component)**

## Installation

You can install `@speclynx/apidom-reference` via [npm CLI](https://docs.npmjs.com/cli) by running the following command:

```sh
 $ npm install @speclynx/apidom-reference
```

## Configurations

This package has two main exports suitable for different use-cases. **Empty** configuration and **saturated** configuration.

### Empty configuration

```js
import { parse } from '@speclynx/apidom-reference/configuration/empty';
import OpenAPIJSON3_1Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-3-1';

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: [new OpenAPIJSON3_1Parser({ allowEmpty: true, sourceMap: false })]
  }
});
```

When using this approach, `options` object is not configured with parsers, resolvers or strategies.
This is suitable for creating **web bundles** and gives you total control of the contents of your bundles.

### Saturated configuration

```js
import { parse } from '@speclynx/apidom-reference';
```
or
```js
import { parse } from '@speclynx/apidom-reference/configuration/saturaged';
```

Both of above imports are equivalent. This approach is suitable for **Node.js** environments.
`options` object is pre-configured with all the parsers, resolvers and strategies.

## Parse component

Parse component consists of implementation of default [parser plugins](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers).
Defaults parser plugin is a specialized wrapper that wraps one of the ApiDOM parser adapter into specialized API.
Standard ApiDOM parser adapter can only parse strings. Parser plugins are capable of parsing local filesystem URIs and network URLs.

**Parsing a file localed on local filesystem:**

```js
import { parse } from '@speclynx/apidom-reference';

await parse('/home/user/oas.json', {
  parse: { mediaType: 'application/openapi+json;version=3.1.2' }
});
```

**Parsing an HTTP(S) URL located on internet:**

```js
import { parse } from '@speclynx/apidom-reference';

await parse('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  parse: { mediaType: 'application/openapi+json;version=3.1.2' }
})
```

Notice how we explicitly pass a `mediaType` parse option. This option is actually **not required**,
but if not provided, the Parse component will try to identify appropriate parser plugin by file contents, and it's extension (`.json`).

What actually happens if you don't provide `mediaType` parse option?

```js
import { parse } from '@speclynx/apidom-reference';

await parse('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json');
```

The result of this operation is going to be generic ApiDOM structure. By analyzing the name of the file
we can identify the extension of the file as `.json`. At this point we only know
that this file is probably going to contain JSON string, though we have no idea what data (AsyncApi/OpenApi)
is encoded within that JSON string.

In the future, we will introduce smart algorithms for looking in the contents of a file and detecting the
`mediaType` automatically. Of course not explicitly providing `mediaType` has performance implications (running detection)
so providing it is always a better option.

### Parser plugins

Parse component comes with number of default parser plugins.

#### [apidom-json](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/apidom-json)

Parses dehydrated ApiDOM structure and hydrates it.
This parser plugin is uniquely identified by `apidom-json` name.

Supported media types are:

```js
[
  'application/vnd.apidom',
  'application/vnd.apidom+json',
]
```

#### [openapi-json-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-json-2)

Wraps [@speclynx/apidom-parser-adapter-openapi-json-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-json-2) package
and is uniquely  identified by `openapi-json-2` name.

Supported media types are:

```js
[
  'application/vnd.oai.openapi;version=2.0',
  'application/vnd.oai.openapi+json;version=2.0',
]
```

#### [openapi-json-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-json-3-0)

Wraps [@speclynx/apidom-parser-adapter-openapi-json-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-json-3-0) package
and is uniquely  identified by `openapi-json-3-0` name.

Supported media types are:

```js
[
  'application/openapi;version=3.0.0',
  'application/openapi+json;version=3.0.0',
  'application/openapi;version=3.0.1',
  'application/openapi+json;version=3.0.1',
  'application/openapi;version=3.0.2',
  'application/openapi+json;version=3.0.2',
  'application/openapi;version=3.0.3',
  'application/openapi+json;version=3.0.3',
  'application/openapi;version=3.0.4',
  'application/openapi+json;version=3.0.4',
]
```

#### [openapi-yaml-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-yaml-2)

Wraps [@speclynx/apidom-parser-adapter-openapi-yaml-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-yaml-2) package
and is uniquely  identified by `openapi-yaml-2` name.

Supported media types are:

```js
[
  'application/vnd.oai.openapi;version=2.0',
  'application/vnd.oai.openapi+yaml;version=2.0',
]
```

#### [openapi-yaml-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-yaml-3-0)

Wraps [@speclynx/apidom-parser-adapter-openapi-yaml-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-yaml-3-0) package
and is uniquely  identified by `openapi-yaml-3-0` name.

Supported media types are:

```js
[
  'application/openapi;version=3.0.0',
  'application/openapi+yaml;version=3.0.0',
  'application/openapi;version=3.0.1',
  'application/openapi+yaml;version=3.0.1',
  'application/openapi;version=3.0.2',
  'application/openapi+yaml;version=3.0.2',
  'application/openapi;version=3.0.3',
  'application/openapi+yaml;version=3.0.3',
  'application/openapi;version=3.0.4',
  'application/openapi+yaml;version=3.0.4',
]
```

#### [openapi-json-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-json-3-1)

Wraps [@speclynx/apidom-parser-adapter-openapi-json-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-json-3-1) package
and is uniquely  identified by `openapi-json-3-1` name.

Supported media types are:

```js
[
  'application/openapi;version=3.1.0',
  'application/openapi+json;version=3.1.0',
  'application/openapi;version=3.1.1',
  'application/openapi+json;version=3.1.1',
  'application/openapi;version=3.1.2',
  'application/openapi+json;version=3.1.2',
]
```

#### [openapi-yaml-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/openapi-yaml-3-1)

Wraps [@speclynx/apidom-parser-adapter-openapi-yaml-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-openapi-yaml-3-1) package
and is uniquely  identified by `openapi-yaml-3-1` name.

Supported media types are:

```js
[
  'application/openapi;version=3.1.0',
  'application/openapi+yaml;version=3.1.0',
  'application/openapi;version=3.1.1',
  'application/openapi+yaml;version=3.1.1',
  'application/openapi;version=3.1.2',
  'application/openapi+yaml;version=3.1.2'
]
```

#### [asyncapi-json-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/asyncapi-json-2)

Wraps [@speclynx/apidom-parser-adapter-asyncapi-json-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-asyncapi-json-2) package
and is uniquely identified by `asyncapi-json-2` name.

Supported media types are:

```js
[
  'application/vnd.aai.asyncapi;version=2.0.0',
  'application/vnd.aai.asyncapi;version=2.1.0',
  'application/vnd.aai.asyncapi;version=2.2.0',
  'application/vnd.aai.asyncapi;version=2.3.0',
  'application/vnd.aai.asyncapi;version=2.4.0',
  'application/vnd.aai.asyncapi;version=2.5.0',
  'application/vnd.aai.asyncapi;version=2.6.0',
  'application/vnd.aai.asyncapi+json;version=2.0.0',
  'application/vnd.aai.asyncapi+json;version=2.1.0',
  'application/vnd.aai.asyncapi+json;version=2.2.0',
  'application/vnd.aai.asyncapi+json;version=2.3.0',
  'application/vnd.aai.asyncapi+json;version=2.4.0',
  'application/vnd.aai.asyncapi+json;version=2.5.0',
  'application/vnd.aai.asyncapi+json;version=2.6.0',
]
```

#### [asyncapi-yaml-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/asyncapi-yaml-2)

Wraps [@speclynx/apidom-parser-adapter-asyncapi-yaml-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-asyncapi-yaml-2) package
and is uniquely  identified by `asyncapi-yaml-2` name.


Supported media types are:

```js
[
  'application/vnd.aai.asyncapi;version=2.0.0',
  'application/vnd.aai.asyncapi;version=2.1.0',
  'application/vnd.aai.asyncapi;version=2.2.0',
  'application/vnd.aai.asyncapi;version=2.3.0',
  'application/vnd.aai.asyncapi;version=2.3.0',
  'application/vnd.aai.asyncapi;version=2.4.0',
  'application/vnd.aai.asyncapi;version=2.5.0',
  'application/vnd.aai.asyncapi;version=2.6.0',
  'application/vnd.aai.asyncapi+yaml;version=2.0.0',
  'application/vnd.aai.asyncapi+yaml;version=2.1.0',
  'application/vnd.aai.asyncapi+yaml;version=2.2.0',
  'application/vnd.aai.asyncapi+yaml;version=2.3.0',
  'application/vnd.aai.asyncapi+yaml;version=2.4.0',
  'application/vnd.aai.asyncapi+yaml;version=2.5.0',
  'application/vnd.aai.asyncapi+yaml;version=2.6.0',
]
```

#### [arazzo-json-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/arazzo-json-1)

Wraps [@speclynx/apidom-parser-adapter-arazzo-json-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-arazzo-json-1) package
and is uniquely identified by `arazzo-json-1` name.

Supported media types are:

```js
[
  'application/vnd.oai.workflows;version=1.0.0',
  'application/vnd.oai.workflows+json;version=1.0.0',
  'application/vnd.oai.workflows;version=1.0.1',
  'application/vnd.oai.workflows+json;version=1.0.1',
]
```

##### Parser options

The following options can be passed via `parse.parserOpts` (globally) or `parse.parserOpts['arazzo-json-1']` (parser-specific).
Parser-specific options take precedence over global options.

- **sourceDescriptions** - Controls which external source descriptions are parsed and included in the `ParseResultElement`.
  - `true` - parse all source descriptions
  - `string[]` - parse only source descriptions with matching names (e.g., `['petStore', 'paymentApi']`)

  Each parsed source description is added with a `'source-description'` class and metadata (`name`, `type`, `retrievalURI`).
  Only OpenAPI 2.0, OpenAPI 3.0.x, OpenAPI 3.1.x, and Arazzo 1.x documents are accepted as source descriptions.
- **sourceDescriptionsMaxDepth** - Maximum recursion depth for parsing nested Arazzo source descriptions.
  Defaults to `+Infinity`. Circular references are automatically detected and skipped.

##### Error handling

The source descriptions parsing uses annotations instead of throwing errors, allowing parsing to continue
even when individual source descriptions fail. Errors are reported as `AnnotationElement` instances
with an `'error'` class within the parse result:

- **Max depth exceeded** - When `sourceDescriptionsMaxDepth` is reached, an error annotation is returned
  instead of the nested source descriptions
- **Parse failures** - If a source description file cannot be parsed (e.g., file not found, invalid syntax),
  an error annotation is returned for that specific source description while other source descriptions
  continue to be processed
- **Validation warnings** - Warning annotations (with `'warning'` class) are returned when prerequisites
  for parsing are not met (e.g., API is not an Arazzo specification)

```js
import { toValue } from '@speclynx/apidom-core';
import { parse } from '@speclynx/apidom-reference';

// Parse all source descriptions
const parseResult = await parse('/path/to/arazzo.json', {
  parse: {
    parserOpts: {
      sourceDescriptions: true,
      sourceDescriptionsMaxDepth: 10,
    },
  },
});

// Parse only specific source descriptions by name
const parseResultFiltered = await parse('/path/to/arazzo.json', {
  parse: {
    parserOpts: {
      sourceDescriptions: ['petStore', 'paymentApi'],
    },
  },
});

// Access parsed source descriptions
for (const element of parseResult) {
  if (element.classes.includes('source-description')) {
    console.log(element.meta.get('name')); // e.g., 'petStore'
    console.log(element.meta.get('type')); // e.g., 'openapi'
    console.log(element.meta.get('retrievalURI')); // e.g., '/path/to/petstore.json'
  }
}
```

##### Accessing parsed documents via SourceDescriptionElement

When source descriptions parsing is enabled, a `ParseResultElement` is attached to each
`SourceDescriptionElement`'s meta as `'parseResult'`. This attachment always happens,
regardless of success or failure:

- **On success**: The parseResult contains the parsed API document
- **On failure**: The parseResult contains error/warning annotations explaining what went wrong

```js
import { toValue } from '@speclynx/apidom-core';
import { parse } from '@speclynx/apidom-reference';

const parseResult = await parse('/path/to/arazzo.json', {
  parse: { parserOpts: { sourceDescriptions: true } },
});

// Access parsed document from source description element
const api = parseResult.api;
const sourceDesc = api.sourceDescriptions.get(0);
const parsedDoc = sourceDesc.meta.get('parseResult');

// Check for errors before using
if (parsedDoc.errors.length > 0) {
  console.log('Parsing failed:', parsedDoc.errors);
} else {
  console.log(parsedDoc.api.element); // e.g., 'openApi3_1'
  console.log(parsedDoc.meta.get('retrievalURI')); // URI where it was fetched from
}
```

##### Low-level API

For advanced use cases where you need to parse source descriptions from an already-parsed Arazzo document
(e.g., when using naked parser adapters directly), the `parseSourceDescriptions` function is exported.

When called directly, this function always parses source descriptions (no need to pass `sourceDescriptions: true`).
Use an array to filter by name:

```js
import { parse } from '@speclynx/apidom-parser-adapter-arazzo-json-1';
import { parseSourceDescriptions } from '@speclynx/apidom-reference/parse/parsers/arazzo-json-1';
import { options, mergeOptions } from '@speclynx/apidom-reference';

// Parse using naked parser adapter
const parseResult = await parse(arazzoJsonString);

// Parse all source descriptions
const sourceDescriptions = await parseSourceDescriptions(
  parseResult,
  '/path/to/arazzo.json',
  options,
);

// Or filter by name
const filtered = await parseSourceDescriptions(
  parseResult,
  '/path/to/arazzo.json',
  mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: ['petStore'] } } }),
);

// Access parsed document from source description element
const sourceDesc = parseResult.api.sourceDescriptions.get(0);
const parsedDoc = sourceDesc.meta.get('parseResult');
```

The function signature is:
```typescript
parseSourceDescriptions(
  parseResult: ParseResultElement,
  parseResultRetrievalURI: string,
  options: ReferenceOptions,
  parserName?: string, // defaults to 'arazzo-json-1'
): Promise<ParseResultElement[]>
```

#### [arazzo-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/arazzo-yaml-1)

Wraps [@speclynx/apidom-parser-adapter-arazzo-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-arazzo-yaml-1) package
and is uniquely identified by `arazzo-yaml-1` name.

Supported media types are:

```js
[
  'application/vnd.oai.workflows;version=1.0.0',
  'application/vnd.oai.workflows+yaml;version=1.0.0',
  'application/vnd.oai.workflows;version=1.0.1',
  'application/vnd.oai.workflows+yaml;version=1.0.1',
]
```

##### Parser options

The following options can be passed via `parse.parserOpts` (globally) or `parse.parserOpts['arazzo-yaml-1']` (parser-specific).
Parser-specific options take precedence over global options. See [arazzo-json-1](#arazzo-json-1) for usage examples.

- **sourceDescriptions** - Controls which external source descriptions are parsed and included in the `ParseResultElement`.
  - `true` - parse all source descriptions
  - `string[]` - parse only source descriptions with matching names (e.g., `['petStore', 'paymentApi']`)

  Each parsed source description is added with a `'source-description'` class and metadata (`name`, `type`, `retrievalURI`).
  Only OpenAPI 2.0, OpenAPI 3.0.x, OpenAPI 3.1.x, and Arazzo 1.x documents are accepted as source descriptions.
- **sourceDescriptionsMaxDepth** - Maximum recursion depth for parsing nested Arazzo source descriptions.
  Defaults to `+Infinity`. Circular references are automatically detected and skipped.

##### Error handling

See [arazzo-json-1 Error handling](#error-handling) - the behavior is identical for both JSON and YAML parsers.

##### Accessing parsed documents via SourceDescriptionElement

See [arazzo-json-1 Accessing parsed documents](#accessing-parsed-documents-via-sourcedescriptionelement) - the behavior is identical for both JSON and YAML parsers.

##### Low-level API

For advanced use cases where you need to parse source descriptions from an already-parsed Arazzo document
(e.g., when using naked parser adapters directly), the `parseSourceDescriptions` function is exported.

When called directly, this function always parses source descriptions (no need to pass `sourceDescriptions: true`).
Use an array to filter by name:

```js
import { parse } from '@speclynx/apidom-parser-adapter-arazzo-yaml-1';
import { parseSourceDescriptions } from '@speclynx/apidom-reference/parse/parsers/arazzo-yaml-1';
import { options, mergeOptions } from '@speclynx/apidom-reference';

// Parse using naked parser adapter
const parseResult = await parse(arazzoYamlString);

// Parse all source descriptions
const sourceDescriptions = await parseSourceDescriptions(
  parseResult,
  '/path/to/arazzo.yaml',
  options,
);

// Or filter by name
const filtered = await parseSourceDescriptions(
  parseResult,
  '/path/to/arazzo.yaml',
  mergeOptions(options, { parse: { parserOpts: { sourceDescriptions: ['petStore'] } } }),
);

// Access parsed document from source description element
const sourceDesc = parseResult.api.sourceDescriptions.get(0);
const parsedDoc = sourceDesc.meta.get('parseResult');
```

The function signature is:
```typescript
parseSourceDescriptions(
  parseResult: ParseResultElement,
  parseResultRetrievalURI: string,
  options: ReferenceOptions,
  parserName?: string, // defaults to 'arazzo-yaml-1'
): Promise<ParseResultElement[]>
```

#### [overlay-json-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/overlay-json-1)

Wraps [@speclynx/apidom-parser-adapter-overlay-json-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-overlay-json-1) package
and is uniquely identified by `overlay-json-1` name.

Supported media types are:

```js
[
  'application/vnd.oai.overlay;version=1.0.0',
  'application/vnd.oai.overlay+json;version=1.0.0',
  'application/vnd.oai.overlay;version=1.1.0',
  'application/vnd.oai.overlay+json;version=1.1.0',
]
```

##### Parser options

The following options can be passed via `parse.parserOpts` (globally) or `parse.parserOpts['overlay-json-1']` (parser-specific).
Parser-specific options take precedence over global options.

- **extends** - Controls whether the `extends` target document is parsed and included in the `ParseResultElement`.
  - `true` - parse the extends target document

  The parsed extends document is added with an `'extends'` class and `retrievalURI` metadata.
  A `ParseResultElement` is also attached to the extends element's meta as `'parseResult'`.

##### Error handling

The extends parsing uses annotations instead of throwing errors, allowing parsing to continue
even when the target document cannot be resolved. Errors are reported as `AnnotationElement` instances
with an `'error'` class within the parse result.

```js
import { parse } from '@speclynx/apidom-reference';

// Parse with extends resolution
const parseResult = await parse('/path/to/overlay.json', {
  parse: {
    parserOpts: {
      'overlay-json-1': { extends: true },
    },
  },
});

// Access parsed extends document
for (const element of parseResult) {
  if (element.classes.includes('extends')) {
    console.log(element.meta.get('retrievalURI')); // e.g., '/path/to/openapi.json'
  }
}
```

##### Accessing parsed document via extends element

When extends parsing is enabled, a `ParseResultElement` is attached to the
extends `StringElement`'s meta as `'parseResult'`. This attachment always happens,
regardless of success or failure:

- **On success**: The parseResult contains the parsed API document
- **On failure**: The parseResult contains error annotations explaining what went wrong

```js
import { parse } from '@speclynx/apidom-reference';

const parseResult = await parse('/path/to/overlay.json', {
  parse: { parserOpts: { 'overlay-json-1': { extends: true } } },
});

// Access parsed document from extends element
const api = parseResult.api;
const extendsElement = api.get('extends');
const parsedDoc = extendsElement.meta.get('parseResult');

// Check for errors before using
if (parsedDoc.errors.length > 0) {
  console.log('Parsing failed:', parsedDoc.errors);
} else {
  console.log(parsedDoc.api.element); // e.g., 'openApi3_1'
  console.log(parsedDoc.meta.get('retrievalURI')); // URI where it was fetched from
}
```

##### Low-level API

For advanced use cases where you need to parse the extends target from an already-parsed Overlay document
(e.g., when using naked parser adapters directly), the `parseExtends` function is exported:

```js
import { parse } from '@speclynx/apidom-parser-adapter-overlay-json-1';
import { parseExtends } from '@speclynx/apidom-reference/parse/parsers/overlay-json-1';
import { options } from '@speclynx/apidom-reference';

// Parse using naked parser adapter
const parseResult = await parse(overlayJsonString);

// Parse the extends target
await parseExtends(parseResult, '/path/to/overlay.json', options);

// Access parsed document from extends element
const extendsElement = parseResult.api.get('extends');
const parsedDoc = extendsElement.meta.get('parseResult');
```

The function signature is:
```typescript
parseExtends(
  parseResult: ParseResultElement,
  parseResultRetrievalURI: string,
  options: ReferenceOptions,
): Promise<void>
```

#### [overlay-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/overlay-yaml-1)

Wraps [@speclynx/apidom-parser-adapter-overlay-yaml-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-overlay-yaml-1) package
and is uniquely identified by `overlay-yaml-1` name.

Supported media types are:

```js
[
  'application/vnd.oai.overlay;version=1.0.0',
  'application/vnd.oai.overlay+yaml;version=1.0.0',
  'application/vnd.oai.overlay;version=1.1.0',
  'application/vnd.oai.overlay+yaml;version=1.1.0',
]
```

##### Parser options

The following options can be passed via `parse.parserOpts` (globally) or `parse.parserOpts['overlay-yaml-1']` (parser-specific).
Parser-specific options take precedence over global options. See [overlay-json-1](#overlay-json-1) for usage examples.

- **extends** - Controls whether the `extends` target document is parsed and included in the `ParseResultElement`.
  - `true` - parse the extends target document

  The parsed extends document is added with an `'extends'` class and `retrievalURI` metadata.
  A `ParseResultElement` is also attached to the extends element's meta as `'parseResult'`.

##### Error handling

See [overlay-json-1 Error handling](#error-handling-2) - the behavior is identical for both JSON and YAML parsers.

##### Accessing parsed document via extends element

See [overlay-json-1 Accessing parsed document](#accessing-parsed-document-via-extends-element) - the behavior is identical for both JSON and YAML parsers.

##### Low-level API

For advanced use cases where you need to parse the extends target from an already-parsed Overlay document
(e.g., when using naked parser adapters directly), the `parseExtends` function is exported:

```js
import { parse } from '@speclynx/apidom-parser-adapter-overlay-yaml-1';
import { parseExtends } from '@speclynx/apidom-reference/parse/parsers/overlay-yaml-1';
import { options } from '@speclynx/apidom-reference';

// Parse using naked parser adapter
const parseResult = await parse(overlayYamlString);

// Parse the extends target
await parseExtends(parseResult, '/path/to/overlay.yaml', options);

// Access parsed document from extends element
const extendsElement = parseResult.api.get('extends');
const parsedDoc = extendsElement.meta.get('parseResult');
```

The function signature is:
```typescript
parseExtends(
  parseResult: ParseResultElement,
  parseResultRetrievalURI: string,
  options: ReferenceOptions,
): Promise<void>
```

#### [json](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/json)

Wraps [@speclynx/apidom-parser-adapter-json](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-json) package
and is uniquely  identified by `json` name.


Supported media types are:

```js
[
  'application/json'
]
```

#### [yaml-1-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/yaml-1-2)

Wraps [@speclynx/apidom-parser-adapter-yaml-1-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-parser-adapter-yaml-1-2) package
and is uniquely  identified by `yaml-1-2` name.


Supported media types are:

```js
[
  'text/yaml',
  'application/yaml'
]
```

#### [binary](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/parse/parsers/binary)

Can parse any binary or non-binary file and return it's content as `base64` encoded string.
This parser is uniquely identified by `binary` name.


**All** media types are supported.

#### Parser plugins execution order

It's important to understand that default parser plugins are run in specific order. The order is determined
by the [options.parse.parsers](https://github.com/speclynx/apidom/blob/ba888d711a4292e8ed0b72e343c4902a4bf0d45a/packages/apidom-reference/src/configuration/saturated.ts#L22) option.
Every plugin is pulled from `options.parse.parsers` option, and it's `canParse` method is called to determine
whether the plugin can parse the URI. If `canParse` returns `true`, `parse` method of plugin is called
and result from parsing is returned. No subsequent parser plugins are run. If `canParse` returns
`false`, next parser plugin is pulled and this process is repeated until one of the parser plugins `canParse` method
returns `true` or until entire list of parser plugins is exhausted (throws error).

```js
[
  new OpenAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIJSON3_0Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML3_0Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIJSON3_1Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML3_1Parser({ allowEmpty: true, sourceMap: false }),
  new AsyncAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
  new AsyncAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
  new ArazzoJSON1Parser({ allowEmpty: true, sourceMap: false }),
  new ArazzoYAML1Parser({ allowEmpty: true, sourceMap: false }),
  new APIDesignSystemsJSONParser({ allowEmpty: true, sourceMap: false }),
  new APIDesignSystemsYAMLParser({ allowEmpty: true, sourceMap: false }),
  new APIDOMJSONParser({ allowEmpty: true, sourceMap: false }),
  new JSONParser({ allowEmpty: true, sourceMap: false }),
  new YAMLParser({ allowEmpty: true, sourceMap: false }),
  new BinaryParser({ allowEmpty: true }),
]
```
Most specific parser plugins are listed first, most generic are listed last.

It's possible to **change** the parser plugins **order globally** by mutating global `parse` options:

```js
import { options } from '@speclynx/apidom-reference';
import OpenAPIJSON2Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-2';
import OpenAPIYAML2Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-2';
import OpenAPIJSON3_0Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-3-0';
import OpenAPIYAML3_0Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-3-0'
import OpenAPIJSON3_1Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-3-1';
import OpenAPIYAML3_1Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-3-1'
import AsyncAPIJSON2Parser from '@speclynx/apidom-reference/parse/parsers/asyncapi-json-2';
import AsyncAPIYAML2Parser from '@speclynx/apidom-reference/parse/parsers/asyncapi-yaml-2';
import ArazzoJSON1Parser from '@speclynx/apidom-reference/parse/parsers/arazzo-json-1';
import ArazzoYAML1Parser from '@speclynx/apidom-reference/parse/parsers/arazzo-yaml-1';
import APIDOMJSONParser from '@speclynx/apidom-reference/parse/parsers/apidom-json';
import JSONParser from '@speclynx/apidom-reference/parse/parsers/json';
import YAMLParser from '@speclynx/apidom-reference/parse/parsers/yaml';
import BinaryParser from '@speclynx/apidom-reference/parse/parsers/binary';


options.parse.parsers = [
  new OpenAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIJSON3_0Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML3_0Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIJSON3_1Parser({ allowEmpty: true, sourceMap: false }),
  new OpenAPIYAML3_1Parser({ allowEmpty: true, sourceMap: false }),
  new AsyncAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
  new AsyncAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
  new ArazzoJSON1Parser({ allowEmpty: true, sourceMap: false }),
  new ArazzoYAML1Parser({ allowEmpty: true, sourceMap: false }),
  new APIDesignSystemsJSONParser({ allowEmpty: true, sourceMap: false }),
  new APIDesignSystemsYAMLParser({ allowEmpty: true, sourceMap: false }),
  new APIDOMJSONParser({ allowEmpty: true, sourceMap: false }),
  new YAMLParser({ allowEmpty: true, sourceMap: false }),
  new JSONParser({ allowEmpty: true, sourceMap: false }),
  new BinaryParser({ allowEmpty: true }),
]
```

To **change** the parser plugins **order** on ad-hoc basis:

```js
import { parse } from '@speclynx/apidom-reference';
import OpenAPIJSON2Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-2';
import OpenAPIYAML2Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-2';
import OpenAPIJSON3_0Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-3-0';
import OpenAPIYAML3_0Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-3-0'
import OpenAPIJSON3_1Parser from '@speclynx/apidom-reference/parse/parsers/openapi-json-3-1';
import OpenAPIYAML3_1Parser from '@speclynx/apidom-reference/parse/parsers/openapi-yaml-3-1'
import AsyncAPIJSON2Parser from '@speclynx/apidom-reference/parse/parsers/asyncapi-json-2';
import AsyncAPIYAML2Parser from '@speclynx/apidom-reference/parse/parsers/asyncapi-yaml-2';
import ArazzoJSON1Parser from '@speclynx/apidom-reference/parse/parsers/arazzo-json-1';
import ArazzoYAML1Parser from '@speclynx/apidom-reference/parse/parsers/arazzo-yaml-1';
import APIDOMJSONParser from '@speclynx/apidom-reference/parse/parsers/apidom-json';
import JSONParser from '@speclynx/apidom-reference/parse/parsers/json';
import YAMLParser from '@speclynx/apidom-reference/parse/parsers/yaml';
import BinaryParser from '@speclynx/apidom-reference/parse/parsers/binary';

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: [
      new OpenAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
      new OpenAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
      new OpenAPIJSON3_0Parser({ allowEmpty: true, sourceMap: false }),
      new OpenAPIYAML3_0Parser({ allowEmpty: true, sourceMap: false }),
      new OpenAPIJSON3_1Parser({ allowEmpty: true, sourceMap: false }),
      new OpenAPIYAML3_1Parser({ allowEmpty: true, sourceMap: false }),
      new AsyncAPIJSON2Parser({ allowEmpty: true, sourceMap: false }),
      new AsyncAPIYAML2Parser({ allowEmpty: true, sourceMap: false }),
      new ArazzoJSON1Parser({ allowEmpty: true, sourceMap: false }),
      new ArazzoYAML1Parser({ allowEmpty: true, sourceMap: false }),
      new APIDesignSystemsJSONParser({ allowEmpty: true, sourceMap: false }),
      new APIDesignSystemsYAMLParser({ allowEmpty: true, sourceMap: false }),
      new APIDOMJSONParser({ allowEmpty: true, sourceMap: false }),
      new JSONParser({ allowEmpty: true, sourceMap: false }),
      new YAMLParser({ allowEmpty: true, sourceMap: false }),
      new BinaryParser({ allowEmpty: true }),
    ],
  },
});
```

#### Parser plugin options

Parser plugins accept additional options like `allowEmpty`, `sourceMap`, or `style`. It's possible to **change** parser plugin
**options globally** by mutating global `parse` options:

```js
import { options, parse } from '@speclynx/apidom-reference';

options.parser.parserOpts = {
  allowEmpty: false,
  sourceMap: true,
  style: true,
};

await parse('/home/user/oas.json', {
  parse: { mediaType: 'application/openapi+json;version=3.1.2' }
});
```

To **change** the parser plugins **options** on ad-hoc basis:

```js
import { parse } from '@speclynx/apidom-reference';

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parserOpts: { allowEmpty: false, sourceMap: true, style: true },
  },
});
```

### Creating new parser plugin

Parse component can be extended by additional parser plugins. Every parser plugin is an object that
must conform to the following interface/shape:

```typescript
interface ParserPlugin {
  // uniquely identifies this parser plugin
  readonly name: string;
  allowEmpty: boolean;
  sourceMap: boolean;
  style: boolean;
  fileExtensions: string[];
  mediaTypes: string[];

  // this method is called to determine whether the parser plugin can parse the file
  canParse(file: File): boolean | Promise<boolean>;

  // this method actually parses the file
  parse(file: File): ParseResultElement | Promise<ParseResultElement>;
}
```

New parser plugin is then provided as an option to a `parse` function:

```ts
import { parse, options, File, Parser, ParserOptions } from '@speclynx/apidom-reference';

interface MyCustomParserPluginOptions extends Omit<ParserOptions, 'name'> {}

class MyCustomParserPlugin extends Parser {
  constructor(options?: MyCustomParserPluginOptions) {
    super({ ...(options ?? {}), name: 'my-custom-parser' });
  }

  async canParse(file: File, options: typeof options) {
    return true;
  }

  async parse(file: File, options: typeof options) {
    // implementation of parsing
  }
}

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: [...options.parse.parsers, new MyCustomParserPlugin()],
  }
});
```

In this particular example we're adding our custom parser plugin as the last plugin
to the available default parser plugin list, so there's a good chance that one of the
default parser plugins detects that it can parse the `/home/user/oas.json` file,
parses it and returns.

If you want to force execution of your custom plugin, add it as a first parser plugin:

```ts
import { parse, options, File, Parser, ParserOptions } from '@speclynx/apidom-reference';

interface MyCustomParserPluginOptions extends Omit<ParserOptions, 'name'> {}

class MyCustomParserPlugin extends Parser {
  constructor(options?: MyCustomParserPluginOptions) {
    super({ ...(options ?? {}), name: 'my-custom-parser' });
  }

  async canParse(file: File, options: typeof options) {
    return true;
  }

  async parse(file: File, options: typeof options) {
    // implementation of parsing
  }
}


await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: [new MyCustomParserPlugin(), ...options.parse.parsers],
  }
});
```

To override the default parser plugins entirely, set `myCustomParserPlugin` plugin to be the only one available:

```ts
import { parse, options, File, Parser, ParserOptions } from '@speclynx/apidom-reference';

interface MyCustomParserPluginOptions extends Omit<ParserOptions, 'name'> {}

class MyCustomParserPlugin extends Parser {
  constructor(options?: MyCustomParserPluginOptions) {
    super({ ...(options ?? {}), name: 'my-custom-parser' });
  }

  async canParse(file: File, options: typeof options) {
    return true;
  }

  async parse(file: File, options: typeof options) {
    // implementation of parsing
  }
}

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: [new MyCustomParserPlugin()],
  }
});
```

### Manipulating parser plugins

Parser plugins can be added, removed, replaced or reordered.

Here are two examples of removing one of the parser plugins called `asyncapi-json-2`.
We're using the fact that every parser plugin is uniquely identifiable by its name.

**Removing** parser plugin **globally** for all subsequence `parse` calls is achieved by mutating global options:

```js
import { parse, options, mergeOptions } from '@speclynx/apidom-reference';

options.parse.parsers = options.parse.parsers.filter(parserPlugin => parserPlugin !== 'asyncapi-json-2')

// here you can be sure `asyncapi-json-2` plugin was disabled
await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
  }
});
```

**Removing** default parser plugin on **ad-hoc** basis:

```js
import { parse, options } from '@speclynx/apidom-reference';

await parse('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
    parsers: options.parse.parsers.filter(parserPlugin => parserPlugin.name !== 'asyncapi-json-2'),
  }
});
```
As you can see, these are all primitive JavaScript Array manipulation techniques.
These techniques can be applied to replacing (use [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)) or reordering parser plugins as well.


## Resolve component

`Resolve component` consists of two (2) sub-components: **File resolution** and **External Resolution**.
`Resolve component` is used by [Parse component](#parse-component) under the hood. `Resolve component` provides a resolved
file contents for a Parse component to parse.

### File resolution

Contains implementation of default [resolver plugins](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/resolvers).
Defaults resolver plugin is an object which knows how to obtain contents of a file represented by URI or URL.

#### Resolver plugins

File resolution comes with two (2) default resolver plugins.

##### [FileResolver](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/resolve/resolvers/file)

This resolver plugin is responsible for resolving a local file.
It detects if the provided URI represents a filesystem path and if so,
reads the file and provides its content.

**WARNING**: use this plugin with caution, as it can read files from a local file system.
By default, this plugin will reject to read any files from the local file system, unless
explicitly provided by **fileAllowList** option.

###### Providing file allow list

File allow list can be provided **globally** as an option to `FileResolver` in form
of array of *glob patterns* or *regular expressions*.

```js
import { options } from '@speclynx/apidom-reference';
import FileResolver from '@speclynx/apidom-reference/resolve/resolvers/file';
import HTTPResolverAxios from '@speclynx/apidom-reference/resolve/resolvers/http-axios';

options.resolve.resolvers = [
  new FileResolver({
    fileAllowList: [
      '*.json',
      /\.json$/,
    ]
  }),
  new HTTPResolverAxios({ timeout: 5000, redirects: 5, withCredentials: false }),
]
```

File allow list can also be provided on ad-hoc basis:

```js
import { resolve } from '@speclynx/apidom-reference';

await resolve('/home/user/oas.json', {
  resolve: {
    resolverOpts: {
      fileAllowList: [
        '*.json',
        /\.json$/,
      ]
    },
  },
});
```

##### [HTTPResolverAxios](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/resolve/resolvers/http-axios)

This resolver plugin is responsible for resolving a remote file represented by HTTP(s) URL.
It detects if the provided URI represents an HTTP(s) URL and if so,
fetches the file and provides its content.

###### [Axios Request Config](https://axios-http.com/docs/req_config) support

HttpResolverAxios plugin supports all the options available in [Axios Request Config](https://axios-http.com/docs/req_config).
Config options can be provided in following way:

```js
import { resolve } from '@speclynx/apidom-reference';

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      axiosConfig: {
        timeout: 10000,
        withCredentials: false,
        responseType: 'json',
      },
    },
  },
});
```

###### [Axios Interceptors](https://axios-http.com/docs/interceptors) support

HttpResolverAxios plugin supports [Axios Interceptors](https://axios-http.com/docs/interceptors).
Interceptors can be provided in following way:

```js
import { resolve } from '@speclynx/apidom-reference';

const requestInterceptor = (config) => config;
const responseInterceptor = (response) => response;

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      axiosConfig: {
        interceptors: {
          request: requestInterceptor,
          response: responseInterceptor,
        },
      },
    },
  },
});
```

Multiple request and response interceptors can be provided in following way:

```js
import { resolve } from '@speclynx/apidom-reference';

const requestInterceptor1 = (config) => config;
const requestInterceptor2 = (config) => config;
const responseInterceptor1 = (response) => response;
const responseInterceptor2 = async (error) => Promise.reject(error);

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      axiosConfig: {
        interceptors: {
          request: [requestInterceptor1, requestInterceptor2],
          response: [responseInterceptor1, responseInterceptor2],
        },
      },
    },
  },
});
```

###### Response caching support

HTTPResolverAxios plugin supports in-memory response caching. When enabled, responses are cached
by URL, and subsequent requests for the same URL are served from cache instead of making
a new HTTP request. Caching is disabled by default.

Enabling cache with default options:

```js
import HTTPResolverAxios from '@speclynx/apidom-reference/resolve/resolvers/http-axios';

const resolver = new HTTPResolverAxios({
  cache: true,
});
```

Enabling cache with custom options:

```js
import HTTPResolverAxios from '@speclynx/apidom-reference/resolve/resolvers/http-axios';

const resolver = new HTTPResolverAxios({
  cache: {
    maxEntries: 512,      // maximum number of entries (default: 1024, false to disable)
    maxStaleAge: 600_000, // maximum age in ms before eviction (default: 3_600_000, false to disable)
  },
});
```

Caching can also be configured via `resolverOpts`:

```js
import { resolve } from '@speclynx/apidom-reference';

// enable with defaults
await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      cache: true,
    },
  },
});

// enable with custom options
await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      cache: {
        maxEntries: 512,
        maxStaleAge: 600_000,
      },
    },
  },
});
```

**File resolution on local filesystem path**:

```js
import { readFile } from '@speclynx/apidom-reference';

await readFile('/home/user/oas.json'); // Promise<Buffer>
```

**File resolution on HTTP(s) URL:**

```js
import { readFile } from '@speclynx/apidom-reference';

await readFile('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json'); // Promise<Buffer>
```
File resolution always returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) containing a [Buffer](https://nodejs.org/api/buffer.html).
It is responsibility of the API consumer to transform `Buffer` into `String` or any other type.

```js
import { readFile } from '@speclynx/apidom-reference';

const buffer = await readFile('/home/user/oas.json');
const string = buffer.toString('utf-8');
```

##### Resolver plugins execution order

It's important to understand that default resolver plugins are run in specific order. The order is determined
by the [options.resolve.resolvers]https://github.com/speclynx/apidom/blob/ba888d711a4292e8ed0b72e343c4902a4bf0d45a/packages/apidom-reference/src/configuration/saturated.ts#L36) option.
Every plugin is pulled from `options.resolve.resolvers` option, and it's `canRead` method is called to determine
whether the plugin can resolve the URI. If `canRead` returns `true`, `read` method of plugin is called
and result from reading the file is returned. No subsequent resolver plugins are run.
If `canRead` returns `false`, next resolver plugin is pulled and this process is repeated until one
of the resolver plugins `canRead` method returns `true` or until entire list of resolver plugins is exhausted (throws error).

```js
[
  new FileResolver(),
  new HTTPResolverAxios({ timeout: 5000, redirects: 5, withCredentials: false }),
]
```

It's possible to **change** resolver plugins **order globally** by mutating global `resolve` option:

```js
import { options } from '@speclynx/apidom-reference';
import FileResolver from '@speclynx/apidom-reference/resolve/resolvers/file';
import HTTPResolverAxios from '@speclynx/apidom-reference/resolve/resolvers/http-axios';

options.resolve.resolvers = [
  new HTTPResolverAxios({ timeout: 5000, redirects: 5, withCredentials: false }),
  new FileResolver(),
]
```

To **change** resolver plugins **order** on ad-hoc basis:

```js
import { readFile } from '@speclynx/apidom-reference';
import FileResolver from '@speclynx/apidom-reference/resolve/resolvers/file';
import HTTPResolverAxios from '@speclynx/apidom-reference/resolve/resolvers/http-axios';

await readFile('/home/user/oas.json', {
  resolve: {
    resolvers: [
      new HTTPResolverAxios({ timeout: 5000, redirects: 5, withCredentials: false }),
      new FileResolver(),
    ],
  },
});
```

##### Resolver plugin options

Some resolver plugins accept additional options. It's possible to **change** resolver plugin
**options globally** by mutating global `resolve` options:

```js
import { options, readFile } from '@speclynx/apidom-reference';

options.resolve.resolverOpts = {
  axiosConfig: {
    timeout: 10000,
  },
};

await readFile('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json');
```

To **change** the resolver plugins **options** on ad-hoc basis:

```js
import { readFile } from '@speclynx/apidom-reference';

await readFile('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    resolverOpts: {
      axiosConfig: {
        timeout: 10000,
      },
    },
  },
});
```

Both of above examples will be using [HttpResolverAxios](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/resolve/resolvers/http-axios) plugin
(as we're trying to resolve HTTP(s) URL) and the `timeout` of resolution will increase from **default 3 seconds**
to 10 seconds.

##### Resolver strategy plugin options

Some resolver strategy plugins accept additional options. It's possible to **change** strategy plugin
**options globally** by mutating global `resolve` options:

```js
import { options, resolve } from '@speclynx/apidom-reference';

options.resolve.strategyOpts = {
  apidom: { clone: true },
};

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json');
```

To **change** the resolver strategy plugins **options** on ad-hoc basis:

```js
import { resolve } from '@speclynx/apidom-reference';

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  resolve: {
    strategyOpts: {
      apidom: { clone: true },
    },
  },
});
```

##### Creating new resolver plugin

Resolve component can be extended by additional resolver plugins. Every resolver plugin is an object that
must conform to the following interface/shape:

```typescript
interface ResolverPlugin {
  // uniquely identifies this plugin
  readonly name: string;

  // this method is called to determine whether the resolver plugin can resolve the file
  canRead(file: File, options: ReferenceOptions): boolean;
  // this method actually resolves the file
  read(file: File, options: ReferenceOptions): Promise<Buffer>;
}
```

New resolver plugin is then provided as an option to a `readFile` function:

```ts
import { readFile, options, File, Resolver, ResolverOptions } from '@speclynx/apidom-reference';

interface MyCustomResolverOptions extends Omit<ResolverOptions, 'name'> {}

class MyCustomResolverPlugin extends Resolver {
  constructor(options?: MyCustomResolverOptions) {
    super({ ...(options ?? {}), name: 'my-custom-resolver' });
  }

  async canRead(file: File, options: typeof options) {
    return true;
  }

  async read(file: File, options: typeof options) {
    // implementation of file resolution
  }
}

await readFile('/home/user/oas.json', {
  resolve: {
    resolvers: [...options.resolve.resolvers, new MyCustomResolverPlugin()],
  }
});
```

In this particular example we're adding our custom resolver plugin as the last plugin
to the available default resolver plugin list, so there's a good chance that one of the
default resolver plugins detects that it can resolve the `/home/user/oas.json` file,
resolves it and returns its content.

If you want to force execution of your custom plugin, add it as a first resolver plugin:

```ts
import { readFile, options, File, Resolver, ResolverOptions } from '@speclynx/apidom-reference';

interface MyCustomResolverOptions extends Omit<ResolverOptions, 'name'> {}

class MyCustomResolverPlugin extends Resolver {
  constructor(options?: MyCustomResolverOptions) {
    super({ ...(options ?? {}), name: 'my-custom-resolver' });
  }

  async canRead(file: File, options: typeof options) {
    return true;
  }

  async read(file: File, options: typeof options) {
    // implementation of file resolution
  }
}

await readFile('/home/user/oas.json', {
  resolve: {
    resolvers: [new MyCustomResolverPlugin(), ...options.resolve.resolvers],
  }
});
```

To override the default resolver plugins entirely, set `myCustomResolverPlugin` plugin to be the only one available:

```ts
import { readFile, options, File, Resolver, ResolverOptions } from '@speclynx/apidom-reference';

interface MyCustomResolverOptions extends Omit<ResolverOptions, 'name'> {}

class MyCustomResolverPlugin extends Resolver {
  constructor(options?: MyCustomResolverOptions) {
    super({ ...(options ?? {}), name: 'my-custom-resolver' });
  }

  async canRead(file: File, options: typeof options) {
    return true;
  }

  async read(file: File, options: typeof options) {
    // implementation of file resolution
  }
}

await readFile('/home/user/oas.json', {
  resolve: {
    resolvers: [new MyCustomResolverPlugin()],
  }
});
```
New resolver plugins can be based on two predefined stamps: [Resolver](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/resolve/resolvers/Resolver.ts) and [HTTPResolver](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/resolve/resolvers/HttpResolver.ts).

##### Manipulating resolver plugins

Resolver plugins can be added, removed, replaced or reordered. We've already covered these techniques in [Manipulating parser plugins section](#manipulating-parser-plugins).

### External resolution

External resolution is a process of resolving all external dependencies of a particular
document using a specific [external resolution strategy](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies). External resolution strategy is determined by
asserting on `mediaType` option. [File Resolution](#file-resolution) (file content is read/fetched)
and [Parse component](#parse-component) (file content is parsed) are used under the hood.

**Externally resolving a file localed on a local filesystem:**

```js
import { resolve } from '@speclynx/apidom-reference';

await resolve('/home/user/oas.json', {
  parse: { mediType: 'application/openapi+json;version=3.1.2' },
}); // Promise<ReferenceSet>
```

**Externally resolving an HTTP(S) URL located on an internet:**

```js
import { resolve } from '@speclynx/apidom-reference';

await resolve('https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.1/webhook-example.json', {
  parse: { mediaType: 'application/openapi+json;version=3.1.2' },
  resolve: {
    resolverOpts: {
      axiosConfig: {
        timeout: 10
      },
    },
  },
}); // Promise<ReferenceSet>
```

**Externally resolving an ApiDOM fragment:**

When externally resolving an ApiDOM fragment, [baseURI](https://github.com/speclynx/apidom/blob/91763fa4ad876375a413e7049c28c2031c7bbe83/apidom/packages/apidom-reference/src/options/index.ts#L47)
resolve option needs to be provided to have a starting point for external dependency resolution.
`mediaType` parse option is unnecessary as we can directly assert the type of ApiDOM fragment.

```js
import { OpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
import { resolveApiDOM } from '@speclynx/apidom-reference';

const apidom = OpenApi3_1Element.refract({
  openapi: '3.1.2',
  components: {
    parameters: {
      externalRef: {
        $ref: './ex.json#/externalParameter', // file is located at /home/user/ex.json
      }
    }
  }
});

const refSet = await resolveApiDOM(apidom, {
  resolve: { baseURI: '/home/user/' },
});

for (const ref of refSet) {
  console.log(ref.uri);
}
// /home/user
// /home/user/ex.json
```

[ReferenceSet](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/ReferenceSet.ts) is a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
like structure containing list of [Reference](https://github.com/speclynx/apidom/blob/main/packages/apidom-reference/src/Reference.ts) objects.
Every Reference object represents single external dependency.

#### [External resolution strategies](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies)

External resolution strategy determines how a document is externally resolved. Depending on document `mediaType`
every strategy differs significantly. Resolve component comes with two (2) default external resolution strategies.

##### [apidom](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/apidom)

External resolution strategy for understanding and resolving remote elements referenced with [Ref Element](https://apielements.org/en/latest/element-definitions.html?highlight=referencing#ref-element).

Supported media types:

```js
[
  'application/vnd.apidom',
  'application/vnd.apidom+json'
]
```

##### [asyncapi-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/asyncapi-2)

External resolution strategy for understanding and resolving external dependencies of [AsyncApi 2.x.y](https://github.com/asyncapi/spec/blob/master/spec/asyncapi.md) definitions.

Supported media types:

```js
[
  'application/vnd.aai.asyncapi;version=2.0.0',
  'application/vnd.aai.asyncapi+json;version=2.0.0',
  'application/vnd.aai.asyncapi+yaml;version=2.0.0',
  'application/vnd.aai.asyncapi;version=2.1.0',
  'application/vnd.aai.asyncapi+json;version=2.1.0',
  'application/vnd.aai.asyncapi+yaml;version=2.1.0',
  'application/vnd.aai.asyncapi;version=2.2.0',
  'application/vnd.aai.asyncapi+json;version=2.2.0',
  'application/vnd.aai.asyncapi+yaml;version=2.2.0',
  'application/vnd.aai.asyncapi;version=2.3.0',
  'application/vnd.aai.asyncapi+json;version=2.3.0',
  'application/vnd.aai.asyncapi+yaml;version=2.3.0',
  'application/vnd.aai.asyncapi;version=2.4.0',
  'application/vnd.aai.asyncapi+json;version=2.4.0',
  'application/vnd.aai.asyncapi+yaml;version=2.4.0',
  'application/vnd.aai.asyncapi;version=2.5.0',
  'application/vnd.aai.asyncapi+json;version=2.5.0',
  'application/vnd.aai.asyncapi+yaml;version=2.5.0',
  'application/vnd.aai.asyncapi;version=2.6.0',
  'application/vnd.aai.asyncapi+json;version=2.6.0',
  'application/vnd.aai.asyncapi+yaml;version=2.6.0',
]
```

##### [arazzo-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/arazzo-1)

External resolution strategy for understanding and resolving external dependencies of [Arazzo 1.x](https://spec.openapis.org/arazzo/latest.html) definitions.

Supported media types:

```js
[
  'application/vnd.oai.workflows;version=1.0.0',
  'application/vnd.oai.workflows+json;version=1.0.0',
  'application/vnd.oai.workflows+yaml;version=1.0.0',
  'application/vnd.oai.workflows;version=1.0.1',
  'application/vnd.oai.workflows+json;version=1.0.1',
  'application/vnd.oai.workflows+yaml;version=1.0.1',
]
```

##### [overlay-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/overlay-1)

External resolution strategy for understanding and resolving external dependencies of [Overlay 1.x](https://spec.openapis.org/overlay/v1.1.0.html) definitions.

Supported media types:

```js
[
  'application/vnd.oai.overlay;version=1.0.0',
  'application/vnd.oai.overlay+json;version=1.0.0',
  'application/vnd.oai.overlay+yaml;version=1.0.0',
  'application/vnd.oai.overlay;version=1.1.0',
  'application/vnd.oai.overlay+json;version=1.1.0',
  'application/vnd.oai.overlay+yaml;version=1.1.0',
]
```

##### [openapi-2](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/openapi-2)

External resolution strategy for understanding and resolving external dependencies of [OpenApi 2.0](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/2.0.md) definitions.

Supported media types:

```js
[
  'application/vnd.oai.openapi;version=2.0',
  'application/vnd.oai.openapi+json;version=2.0',
  'application/vnd.oai.openapi+yaml;version=2.0',
]
```

##### [openapi-3-0](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/openapi-3-0)

External resolution strategy for understanding and resolving external dependencies of [OpenApi 3.0.x](https://spec.openapis.org/oas/v3.0.4.html) definitions.

Supported media types:

```js
[
  'application/openapi;version=3.0.0',
  'application/openapi+json;version=3.0.0',
  'application/openapi+yaml;version=3.0.0',
  'application/openapi;version=3.0.1',
  'application/openapi+json;version=3.0.1',
  'application/openapi+yaml;version=3.0.1',
  'application/openapi;version=3.0.2',
  'application/openapi+json;version=3.0.2',
  'application/openapi+yaml;version=3.0.2',
  'application/openapi;version=3.0.3',
  'application/openapi+json;version=3.0.3',
  'application/openapi+yaml;version=3.0.3',
  'application/openapi;version=3.0.4',
  'application/openapi+json;version=3.0.4',
  'application/openapi+yaml;version=3.0.4',
]
```

##### [openapi-3-1](https://github.com/speclynx/apidom/tree/main/packages/apidom-reference/src/resolve/strategies/openapi-3-1)

External resolution strategy for understanding and resolving external dependencies of [OpenApi 3.1.x](https://spec.openapis.org/oas/v3.1.2.html) definitions.

Supported media types:

```js
[
  'application/openapi;version=3.1.0',
  'application/openapi+json;version=3.1.0',
  'application/openapi+yaml;version=3.1.0',
  'application/openapi;version=3.1.1',
  'application/openapi+json;version=3.1.1',
  'application/openapi+yaml;version=3.1.1',
  'application/openapi;version=3.1.2',
  'application/openapi+json;version=3.1.2',
  'application/openapi+yaml;version=3.1.2'
]
```

##### External resolution strategies execution order

It's important to understand that default external resolution strategies are run in specific order. The order is determined
by the [options.resolve.strategies](https://github.com/speclynx/apidom/blob/ba888d711a4292e8ed0b72e343c4902a4bf0d45a/packages/apidom-reference/src/configuration/saturated.ts#L41) option.
Every strategy is pulled from `options.resolve.strategies` option and its `canResolve` method is called to determine
whether the strategy can externally resolve the URI. If `canResolve` returns `true`, `resolve` method of strategy is called
and result from external resolution is returned. No subsequent strategies  are run. If `canResolve` returns
`false`, next strategy is pulled and this process is repeated until one of the strategy's `canResolve` method
returns `true` or until entire list of strategies is exhausted (throws error).

```js
[
  new OpenAPI2ResolveStrategy(),
  new OpenAPI3_0ResolveStrategy(),
  new OpenAPI3_1ResolveStrategy(),
  new AsyncAPI2ResolveStrategy(),
]
```
Most specific strategies are listed first, most generic are listed last.

It's possible to **change** strategies **order globally** by mutating global `resolve` option:

```js
import { options } from '@speclynx/apidom-reference';
import AsyncAPI2ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/asyncapi-2';
import OpenAPI2ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-2';
import OpenAPI3_0ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-0';
import OpenAPI3_1ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-1';

options.resolve.strategies = [
  new OpenAPI2ResolveStrategy(),
  new OpenAPI3_0ResolveStrategy(),
  new OpenAPI3_1ResolveStrategy(),
  new AsyncAPI2ResolveStrategy(),
];
```

To **change** the strategies **order** on ad-hoc basis:

```js
import { resolve } from '@speclynx/apidom-reference';
import AsyncAPI2ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/asyncapi-2';
import OpenAPI2ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-2';
import OpenAPI3_0ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-0';
import OpenAPI3_1ResolveStrategy from '@speclynx/apidom-reference/resolve/strategies/openapi-3-1';


await resolve('/home/user/oas.json', {
  parse: {
    mediaType: 'application/openapi+json;version=3.1.2',
  },
  resolve: {
    strategies: [
      new AsyncAPI2ResolveStrategy(),
      new OpenAPI2ResolveStrategy(),
      new OpenAPI3_0ResolveStrategy(),
      new OpenAPI3_1ResolveStrategy(),
    ]
  }
});
```
##### Creating new external resolution strategy

Resolve component can be extended by additional strategies. Every strategy is an object that
must conform to the following interface/shape:

```typescript
interface ResolveStrategy {
  // uniquely identifies this plugin
  readonly name: string;

  // this method is called to determine whether the strategy can externally resolve the file
  canResolve(file: File, options: ReferenceOptions): boolean;
  // this method actually externally resolves the file
  resolve(file: File, options: ReferenceOptions): Promise<ReferenceSet>;
}
```

New strategy is then provided as an option to a `resolve` function:

```ts
import { resolve, options, File, ResolveStrategy, ResolveStrategyOptions } from '@speclynx/apidom-reference';

interface MyCustomResolverStrategyOptions extends Omit<ResolveStrategyOptions, 'name'> {}

class MyCustomResolverStrategy extends ResolveStrategy {
  constructor(options?: MyCustomResolverStrategyOptions) {
    super({ ...(options ??