@speclynx/apidom-reference 2.5.1 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +109 -6
  3. package/dist/apidom-reference.browser.js +78639 -78397
  4. package/dist/apidom-reference.browser.min.js +1 -1
  5. package/package.json +25 -25
  6. package/src/bundle/index.cjs +1 -1
  7. package/src/bundle/index.mjs +1 -1
  8. package/src/configuration/saturated.cjs +4 -2
  9. package/src/configuration/saturated.mjs +5 -3
  10. package/src/dereference/index.cjs +1 -1
  11. package/src/dereference/index.mjs +1 -1
  12. package/src/dereference/strategies/openapi-3-0/visitor.cjs +1 -1
  13. package/src/dereference/strategies/openapi-3-0/visitor.mjs +1 -1
  14. package/src/dereference/strategies/openapi-3-1/visitor.cjs +1 -1
  15. package/src/dereference/strategies/openapi-3-1/visitor.mjs +1 -1
  16. package/src/errors/InvalidJsonSchema$anchorError.cjs +1 -1
  17. package/src/errors/InvalidJsonSchema$anchorError.mjs +1 -1
  18. package/src/errors/UnmatchedParserError.cjs +11 -0
  19. package/src/errors/UnmatchedParserError.mjs +6 -0
  20. package/src/index.cjs +3 -1
  21. package/src/index.mjs +1 -0
  22. package/src/parse/index.cjs +3 -3
  23. package/src/parse/index.mjs +3 -3
  24. package/src/parse/parsers/arazzo-json-1/index.cjs +12 -2
  25. package/src/parse/parsers/arazzo-json-1/index.mjs +14 -4
  26. package/src/parse/parsers/arazzo-json-1/source-description.cjs +177 -0
  27. package/src/parse/parsers/arazzo-json-1/source-description.mjs +171 -0
  28. package/src/parse/parsers/arazzo-yaml-1/index.cjs +12 -2
  29. package/src/parse/parsers/arazzo-yaml-1/index.mjs +12 -2
  30. package/src/resolve/index.cjs +2 -2
  31. package/src/resolve/index.mjs +2 -2
  32. package/src/resolve/strategies/apidom/index.cjs +1 -1
  33. package/src/resolve/strategies/apidom/index.mjs +1 -1
  34. package/src/resolve/strategies/asyncapi-2/index.cjs +1 -1
  35. package/src/resolve/strategies/asyncapi-2/index.mjs +1 -1
  36. package/src/resolve/strategies/openapi-2/index.cjs +1 -1
  37. package/src/resolve/strategies/openapi-2/index.mjs +1 -1
  38. package/src/resolve/strategies/openapi-3-0/index.cjs +1 -1
  39. package/src/resolve/strategies/openapi-3-0/index.mjs +1 -1
  40. package/src/resolve/strategies/openapi-3-1/index.cjs +1 -1
  41. package/src/resolve/strategies/openapi-3-1/index.mjs +1 -1
  42. package/src/resolve/util.cjs +2 -2
  43. package/src/resolve/util.mjs +2 -2
  44. package/types/apidom-reference.d.ts +10 -4
  45. package/types/errors/UnmatchedParserError.d.ts +7 -0
  46. package/types/index.d.ts +1 -0
  47. package/types/parse/parsers/Parser.d.ts +3 -2
  48. package/types/parse/parsers/arazzo-json-1/index.d.ts +5 -1
  49. package/types/parse/parsers/arazzo-json-1/source-description.d.ts +13 -0
  50. package/types/parse/parsers/arazzo-yaml-1/index.d.ts +5 -1
  51. package/types/resolve/resolvers/Resolver.d.ts +3 -2
@@ -0,0 +1,171 @@
1
+ import { ParseResultElement, AnnotationElement, isArrayElement, isStringElement, cloneDeep } from '@speclynx/apidom-datamodel';
2
+ import { isArazzoSpecification1Element, isSourceDescriptionElement } from '@speclynx/apidom-ns-arazzo-1';
3
+ import { isSwaggerElement } from '@speclynx/apidom-ns-openapi-2';
4
+ import { isOpenApi3_0Element } from '@speclynx/apidom-ns-openapi-3-0';
5
+ import { isOpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
6
+ import { toValue } from '@speclynx/apidom-core';
7
+ import * as url from "../../../util/url.mjs";
8
+ import { merge as mergeOptions } from "../../../options/util.mjs"; // shared key for recursion state (works across JSON/YAML parsers)
9
+ const ARAZZO_RECURSION_KEY = 'arazzo-1';
10
+ /**
11
+ * Parses a single source description element.
12
+ * Returns ParseResultElement on success, or undefined if skipped.
13
+ */
14
+ async function parseSourceDescription(sourceDescription, ctx) {
15
+ const parseResult = new ParseResultElement();
16
+ if (!isSourceDescriptionElement(sourceDescription)) {
17
+ const annotation = new AnnotationElement('Element is not a valid SourceDescriptionElement. Skipping.');
18
+ annotation.classes.push('warning');
19
+ parseResult.push(annotation);
20
+ return parseResult;
21
+ }
22
+
23
+ // set class and metadata from source description element
24
+ parseResult.classes.push('source-description');
25
+ if (isStringElement(sourceDescription.name)) parseResult.setMetaProperty('name', cloneDeep(sourceDescription.name));
26
+ if (isStringElement(sourceDescription.type)) parseResult.setMetaProperty('type', cloneDeep(sourceDescription.type));
27
+ const sourceDescriptionURI = toValue(sourceDescription.url);
28
+ if (typeof sourceDescriptionURI !== 'string') {
29
+ const annotation = new AnnotationElement('Source description URL is missing or not a string. Skipping.');
30
+ annotation.classes.push('warning');
31
+ parseResult.push(annotation);
32
+ return parseResult;
33
+ }
34
+ const retrievalURI = url.resolve(ctx.baseURI, sourceDescriptionURI);
35
+
36
+ // skip if already visited (cycle detection)
37
+ if (ctx.visitedUrls.has(retrievalURI)) {
38
+ const annotation = new AnnotationElement(`Source description "${retrievalURI}" has already been visited. Skipping to prevent cycle.`);
39
+ annotation.classes.push('warning');
40
+ parseResult.push(annotation);
41
+ return parseResult;
42
+ }
43
+ ctx.visitedUrls.add(retrievalURI);
44
+ try {
45
+ const sdParseResult = await ctx.parseFn(retrievalURI, mergeOptions(ctx.options, {
46
+ parse: {
47
+ mediaType: 'text/plain',
48
+ // allow parser plugin detection
49
+ parserOpts: {
50
+ [ARAZZO_RECURSION_KEY]: {
51
+ sourceDescriptionsDepth: ctx.currentDepth + 1,
52
+ sourceDescriptionsVisitedUrls: ctx.visitedUrls
53
+ }
54
+ }
55
+ }
56
+ }));
57
+ // merge parsed result into our parse result
58
+ for (const item of sdParseResult) {
59
+ parseResult.push(item);
60
+ }
61
+ } catch (error) {
62
+ // create error annotation instead of failing entire parse
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ const annotation = new AnnotationElement(`Error parsing source description "${retrievalURI}": ${message}`);
65
+ annotation.classes.push('error');
66
+ parseResult.push(annotation);
67
+ return parseResult;
68
+ }
69
+
70
+ // only allow OpenAPI and Arazzo as source descriptions
71
+ const {
72
+ api: sourceDescriptionAPI
73
+ } = parseResult;
74
+ const isOpenApi = isSwaggerElement(sourceDescriptionAPI) || isOpenApi3_0Element(sourceDescriptionAPI) || isOpenApi3_1Element(sourceDescriptionAPI);
75
+ const isArazzo = isArazzoSpecification1Element(sourceDescriptionAPI);
76
+ if (!isOpenApi && !isArazzo) {
77
+ const annotation = new AnnotationElement(`Source description "${retrievalURI}" is not an OpenAPI or Arazzo document. Skipping.`);
78
+ annotation.classes.push('warning');
79
+ parseResult.push(annotation);
80
+ return parseResult;
81
+ }
82
+
83
+ // validate declared type matches actual parsed type
84
+ const declaredType = toValue(sourceDescription.type);
85
+ if (typeof declaredType === 'string') {
86
+ if (declaredType === 'openapi' && !isOpenApi) {
87
+ const annotation = new AnnotationElement(`Source description "${retrievalURI}" declared as "openapi" but parsed as Arazzo document.`);
88
+ annotation.classes.push('warning');
89
+ parseResult.push(annotation);
90
+ } else if (declaredType === 'arazzo' && !isArazzo) {
91
+ const annotation = new AnnotationElement(`Source description "${retrievalURI}" declared as "arazzo" but parsed as OpenAPI document.`);
92
+ annotation.classes.push('warning');
93
+ parseResult.push(annotation);
94
+ }
95
+ }
96
+ return parseResult;
97
+ }
98
+
99
+ /**
100
+ * Shared function for parsing source descriptions.
101
+ * Call with `.call(this, ...)` where `this` has `name` and `parseFn` properties.
102
+ * @public
103
+ */
104
+ export async function parseSourceDescriptions(api, file, options) {
105
+ const results = [];
106
+
107
+ /**
108
+ * Validate prerequisites for parsing source descriptions.
109
+ * Return warning annotations if validation fails.
110
+ */
111
+ if (!isArazzoSpecification1Element(api)) {
112
+ const annotation = new AnnotationElement('Cannot parse source descriptions: API is not an Arazzo specification.');
113
+ annotation.classes.push('warning');
114
+ return [new ParseResultElement([annotation])];
115
+ }
116
+ if (!isArrayElement(api.sourceDescriptions)) {
117
+ const annotation = new AnnotationElement('Cannot parse source descriptions: sourceDescriptions field is missing or not an array.');
118
+ annotation.classes.push('warning');
119
+ return [new ParseResultElement([annotation])];
120
+ }
121
+ if (typeof this.parseFn !== 'function') {
122
+ const annotation = new AnnotationElement('Source descriptions found but parseFn is not configured. Skipping source description parsing.');
123
+ annotation.classes.push('error');
124
+ return [new ParseResultElement([annotation])];
125
+ }
126
+
127
+ // user config: parser-specific options take precedence over global parserOpts
128
+ const maxDepth = options?.parse?.parserOpts?.[this.name]?.sourceDescriptionsMaxDepth ?? options?.parse?.parserOpts?.sourceDescriptionsMaxDepth ?? +Infinity;
129
+
130
+ // recursion state comes from shared key (works across JSON/YAML)
131
+ const sharedOpts = options?.parse?.parserOpts?.[ARAZZO_RECURSION_KEY] ?? {};
132
+ const currentDepth = sharedOpts.sourceDescriptionsDepth ?? 0;
133
+ const visitedUrls = sharedOpts.sourceDescriptionsVisitedUrls ?? new Set();
134
+
135
+ // add current file to visited URLs to prevent cycles
136
+ visitedUrls.add(file.uri);
137
+ if (currentDepth >= maxDepth) {
138
+ const annotation = new AnnotationElement(`Maximum parse depth of ${maxDepth} has been exceeded by file "${file.uri}"`);
139
+ annotation.classes.push('error');
140
+ const parseResult = new ParseResultElement([annotation]);
141
+ parseResult.classes.push('source-description');
142
+ return [parseResult];
143
+ }
144
+ const ctx = {
145
+ parseFn: this.parseFn,
146
+ baseURI: file.uri,
147
+ options,
148
+ currentDepth,
149
+ visitedUrls
150
+ };
151
+
152
+ // determine which source descriptions to parse
153
+ const sourceDescriptionsOption = options?.parse?.parserOpts?.[this.name]?.sourceDescriptions ?? options?.parse?.parserOpts?.sourceDescriptions;
154
+
155
+ // handle false or other falsy values - no source descriptions should be parsed
156
+ if (!sourceDescriptionsOption) {
157
+ return results;
158
+ }
159
+ const sourceDescriptions = Array.isArray(sourceDescriptionsOption) ? api.sourceDescriptions.filter(sd => {
160
+ if (!isSourceDescriptionElement(sd)) return false;
161
+ const name = toValue(sd.name);
162
+ return typeof name === 'string' && sourceDescriptionsOption.includes(name);
163
+ }) : api.sourceDescriptions;
164
+
165
+ // process sequentially to ensure proper cycle detection with shared visitedUrls
166
+ for (const sourceDescription of sourceDescriptions) {
167
+ const sourceDescriptionParseResult = await parseSourceDescription(sourceDescription, ctx);
168
+ results.push(sourceDescriptionParseResult);
169
+ }
170
+ return results;
171
+ }
@@ -7,6 +7,7 @@ var _ramda = require("ramda");
7
7
  var _apidomParserAdapterArazzoYaml = require("@speclynx/apidom-parser-adapter-arazzo-yaml-1");
8
8
  var _ParserError = _interopRequireDefault(require("../../../errors/ParserError.cjs"));
9
9
  var _Parser = _interopRequireDefault(require("../Parser.cjs"));
10
+ var _sourceDescription = require("../arazzo-json-1/source-description.cjs");
10
11
  /**
11
12
  * @public
12
13
  */
@@ -16,8 +17,10 @@ var _Parser = _interopRequireDefault(require("../Parser.cjs"));
16
17
  */
17
18
  class ArazzoYAML1Parser extends _Parser.default {
18
19
  refractorOpts;
20
+ parseFn;
19
21
  constructor(options) {
20
22
  const {
23
+ parseFn,
21
24
  fileExtensions = [],
22
25
  mediaTypes = _apidomParserAdapterArazzoYaml.mediaTypes,
23
26
  ...rest
@@ -28,6 +31,7 @@ class ArazzoYAML1Parser extends _Parser.default {
28
31
  fileExtensions,
29
32
  mediaTypes
30
33
  });
34
+ this.parseFn = parseFn;
31
35
  }
32
36
  async canParse(file) {
33
37
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
@@ -39,11 +43,17 @@ class ArazzoYAML1Parser extends _Parser.default {
39
43
  }
40
44
  return false;
41
45
  }
42
- async parse(file) {
46
+ async parse(file, options) {
43
47
  const source = file.toString();
44
48
  try {
45
49
  const parserOpts = (0, _ramda.pick)(['sourceMap', 'strict', 'refractorOpts'], this);
46
- return await (0, _apidomParserAdapterArazzoYaml.parse)(source, parserOpts);
50
+ const parseResult = await (0, _apidomParserAdapterArazzoYaml.parse)(source, parserOpts);
51
+ const shouldParseSourceDescriptions = options?.parse?.parserOpts?.[this.name]?.sourceDescriptions ?? options?.parse?.parserOpts?.sourceDescriptions;
52
+ if (shouldParseSourceDescriptions) {
53
+ const sourceDescriptions = await _sourceDescription.parseSourceDescriptions.call(this, parseResult.api, file, options);
54
+ parseResult.push(...sourceDescriptions);
55
+ }
56
+ return parseResult;
47
57
  } catch (error) {
48
58
  throw new _ParserError.default(`Error parsing "${file.uri}"`, {
49
59
  cause: error
@@ -2,6 +2,7 @@ import { pick } from 'ramda';
2
2
  import { parse, mediaTypes as ArazzoYAML1MediaTypes, detect } from '@speclynx/apidom-parser-adapter-arazzo-yaml-1';
3
3
  import ParserError from "../../../errors/ParserError.mjs";
4
4
  import Parser from "../Parser.mjs";
5
+ import { parseSourceDescriptions } from "../arazzo-json-1/source-description.mjs";
5
6
  /**
6
7
  * @public
7
8
  */
@@ -10,8 +11,10 @@ import Parser from "../Parser.mjs";
10
11
  */
11
12
  class ArazzoYAML1Parser extends Parser {
12
13
  refractorOpts;
14
+ parseFn;
13
15
  constructor(options) {
14
16
  const {
17
+ parseFn,
15
18
  fileExtensions = [],
16
19
  mediaTypes = ArazzoYAML1MediaTypes,
17
20
  ...rest
@@ -22,6 +25,7 @@ class ArazzoYAML1Parser extends Parser {
22
25
  fileExtensions,
23
26
  mediaTypes
24
27
  });
28
+ this.parseFn = parseFn;
25
29
  }
26
30
  async canParse(file) {
27
31
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
@@ -33,11 +37,17 @@ class ArazzoYAML1Parser extends Parser {
33
37
  }
34
38
  return false;
35
39
  }
36
- async parse(file) {
40
+ async parse(file, options) {
37
41
  const source = file.toString();
38
42
  try {
39
43
  const parserOpts = pick(['sourceMap', 'strict', 'refractorOpts'], this);
40
- return await parse(source, parserOpts);
44
+ const parseResult = await parse(source, parserOpts);
45
+ const shouldParseSourceDescriptions = options?.parse?.parserOpts?.[this.name]?.sourceDescriptions ?? options?.parse?.parserOpts?.sourceDescriptions;
46
+ if (shouldParseSourceDescriptions) {
47
+ const sourceDescriptions = await parseSourceDescriptions.call(this, parseResult.api, file, options);
48
+ parseResult.push(...sourceDescriptions);
49
+ }
50
+ return parseResult;
41
51
  } catch (error) {
42
52
  throw new ParserError(`Error parsing "${file.uri}"`, {
43
53
  cause: error
@@ -35,9 +35,9 @@ const resolveApiDOM = async (element, options) => {
35
35
  });
36
36
  const resolveStrategies = await plugins.filter('canResolve', [file, options], options.resolve.strategies);
37
37
 
38
- // we couldn't find any resolver for this File
38
+ // we couldn't find any resolve strategy for this File
39
39
  if ((0, _ramda.isEmpty)(resolveStrategies)) {
40
- throw new _UnmatchedResolveStrategyError.default(file.uri);
40
+ throw new _UnmatchedResolveStrategyError.default(`Could not find a resolve strategy that can resolve the file "${file.uri}"`);
41
41
  }
42
42
  try {
43
43
  const {
@@ -29,9 +29,9 @@ export const resolveApiDOM = async (element, options) => {
29
29
  });
30
30
  const resolveStrategies = await plugins.filter('canResolve', [file, options], options.resolve.strategies);
31
31
 
32
- // we couldn't find any resolver for this File
32
+ // we couldn't find any resolve strategy for this File
33
33
  if (isEmpty(resolveStrategies)) {
34
- throw new UnmatchedResolveStrategyError(file.uri);
34
+ throw new UnmatchedResolveStrategyError(`Could not find a resolve strategy that can resolve the file "${file.uri}"`);
35
35
  }
36
36
  try {
37
37
  const {
@@ -31,7 +31,7 @@ class ApiDOMResolveStrategy extends _ResolveStrategy.default {
31
31
  async resolve(file, options) {
32
32
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'apidom');
33
33
  if (dereferenceStrategy === undefined) {
34
- throw new _UnmatchedDereferenceStrategyError.default('"apidom" dereference strategy is not available.');
34
+ throw new _UnmatchedDereferenceStrategyError.default(`"apidom" resolve strategy requires "apidom" dereference strategy to be configured for file "${file.uri}"`);
35
35
  }
36
36
  const refSet = new _ReferenceSet.default();
37
37
  const mergedOptions = (0, _util.merge)(options, {
@@ -25,7 +25,7 @@ class ApiDOMResolveStrategy extends ResolveStrategy {
25
25
  async resolve(file, options) {
26
26
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'apidom');
27
27
  if (dereferenceStrategy === undefined) {
28
- throw new UnmatchedDereferenceStrategyError('"apidom" dereference strategy is not available.');
28
+ throw new UnmatchedDereferenceStrategyError(`"apidom" resolve strategy requires "apidom" dereference strategy to be configured for file "${file.uri}"`);
29
29
  }
30
30
  const refSet = new ReferenceSet();
31
31
  const mergedOptions = mergeOptions(options, {
@@ -31,7 +31,7 @@ class AsyncAPI2ResolveStrategy extends _ResolveStrategy.default {
31
31
  async resolve(file, options) {
32
32
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'asyncapi-2');
33
33
  if (dereferenceStrategy === undefined) {
34
- throw new _UnmatchedDereferenceStrategyError.default('"asyncapi-2" dereference strategy is not available.');
34
+ throw new _UnmatchedDereferenceStrategyError.default(`"asyncapi-2" resolve strategy requires "asyncapi-2" dereference strategy to be configured for file "${file.uri}"`);
35
35
  }
36
36
  const refSet = new _ReferenceSet.default();
37
37
  const mergedOptions = (0, _util.merge)(options, {
@@ -25,7 +25,7 @@ class AsyncAPI2ResolveStrategy extends ResolveStrategy {
25
25
  async resolve(file, options) {
26
26
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'asyncapi-2');
27
27
  if (dereferenceStrategy === undefined) {
28
- throw new UnmatchedDereferenceStrategyError('"asyncapi-2" dereference strategy is not available.');
28
+ throw new UnmatchedDereferenceStrategyError(`"asyncapi-2" resolve strategy requires "asyncapi-2" dereference strategy to be configured for file "${file.uri}"`);
29
29
  }
30
30
  const refSet = new ReferenceSet();
31
31
  const mergedOptions = mergeOptions(options, {
@@ -31,7 +31,7 @@ class OpenAPI2ResolveStrategy extends _ResolveStrategy.default {
31
31
  async resolve(file, options) {
32
32
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-2');
33
33
  if (dereferenceStrategy === undefined) {
34
- throw new _UnmatchedDereferenceStrategyError.default('"openapi-2" dereference strategy is not available.');
34
+ throw new _UnmatchedDereferenceStrategyError.default(`"openapi-2" resolve strategy requires "openapi-2" dereference strategy to be configured for file "${file.uri}"`);
35
35
  }
36
36
  const refSet = new _ReferenceSet.default();
37
37
  const mergedOptions = (0, _util.merge)(options, {
@@ -25,7 +25,7 @@ class OpenAPI2ResolveStrategy extends ResolveStrategy {
25
25
  async resolve(file, options) {
26
26
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-2');
27
27
  if (dereferenceStrategy === undefined) {
28
- throw new UnmatchedDereferenceStrategyError('"openapi-2" dereference strategy is not available.');
28
+ throw new UnmatchedDereferenceStrategyError(`"openapi-2" resolve strategy requires "openapi-2" dereference strategy to be configured for file "${file.uri}"`);
29
29
  }
30
30
  const refSet = new ReferenceSet();
31
31
  const mergedOptions = mergeOptions(options, {
@@ -31,7 +31,7 @@ class OpenAPI3_0ResolveStrategy extends _ResolveStrategy.default {
31
31
  async resolve(file, options) {
32
32
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-3-0');
33
33
  if (dereferenceStrategy === undefined) {
34
- throw new _UnmatchedDereferenceStrategyError.default('"openapi-3-0" dereference strategy is not available.');
34
+ throw new _UnmatchedDereferenceStrategyError.default(`"openapi-3-0" resolve strategy requires "openapi-3-0" dereference strategy to be configured for file "${file.uri}"`);
35
35
  }
36
36
  const refSet = new _ReferenceSet.default();
37
37
  const mergedOptions = (0, _util.merge)(options, {
@@ -25,7 +25,7 @@ class OpenAPI3_0ResolveStrategy extends ResolveStrategy {
25
25
  async resolve(file, options) {
26
26
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-3-0');
27
27
  if (dereferenceStrategy === undefined) {
28
- throw new UnmatchedDereferenceStrategyError('"openapi-3-0" dereference strategy is not available.');
28
+ throw new UnmatchedDereferenceStrategyError(`"openapi-3-0" resolve strategy requires "openapi-3-0" dereference strategy to be configured for file "${file.uri}"`);
29
29
  }
30
30
  const refSet = new ReferenceSet();
31
31
  const mergedOptions = mergeOptions(options, {
@@ -31,7 +31,7 @@ class OpenAPI3_1ResolveStrategy extends _ResolveStrategy.default {
31
31
  async resolve(file, options) {
32
32
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-3-1');
33
33
  if (dereferenceStrategy === undefined) {
34
- throw new _UnmatchedDereferenceStrategyError.default('"openapi-3-1" dereference strategy is not available.');
34
+ throw new _UnmatchedDereferenceStrategyError.default(`"openapi-3-1" resolve strategy requires "openapi-3-1" dereference strategy to be configured for file "${file.uri}"`);
35
35
  }
36
36
  const refSet = new _ReferenceSet.default();
37
37
  const mergedOptions = (0, _util.merge)(options, {
@@ -25,7 +25,7 @@ class OpenAPI3_1ResolveStrategy extends ResolveStrategy {
25
25
  async resolve(file, options) {
26
26
  const dereferenceStrategy = options.dereference.strategies.find(strategy => strategy.name === 'openapi-3-1');
27
27
  if (dereferenceStrategy === undefined) {
28
- throw new UnmatchedDereferenceStrategyError('"openapi-3-1" dereference strategy is not available.');
28
+ throw new UnmatchedDereferenceStrategyError(`"openapi-3-1" resolve strategy requires "openapi-3-1" dereference strategy to be configured for file "${file.uri}"`);
29
29
  }
30
30
  const refSet = new ReferenceSet();
31
31
  const mergedOptions = mergeOptions(options, {
@@ -21,12 +21,12 @@ const readFile = async (file, options) => {
21
21
 
22
22
  // we couldn't find any resolver for this File
23
23
  if ((0, _ramda.isEmpty)(resolvers)) {
24
- throw new _UnmatchedResolverError.default(file.uri);
24
+ throw new _UnmatchedResolverError.default(`Could not find a resolver that can read the file "${file.uri}"`);
25
25
  }
26
26
  try {
27
27
  const {
28
28
  result
29
- } = await plugins.run('read', [file], resolvers);
29
+ } = await plugins.run('read', [file, options], resolvers);
30
30
  return result;
31
31
  } catch (error) {
32
32
  throw new _ResolveError.default(`Error while reading file "${file.uri}"`, {
@@ -15,12 +15,12 @@ export const readFile = async (file, options) => {
15
15
 
16
16
  // we couldn't find any resolver for this File
17
17
  if (isEmpty(resolvers)) {
18
- throw new UnmatchedResolverError(file.uri);
18
+ throw new UnmatchedResolverError(`Could not find a resolver that can read the file "${file.uri}"`);
19
19
  }
20
20
  try {
21
21
  const {
22
22
  result
23
- } = await plugins.run('read', [file], resolvers);
23
+ } = await plugins.run('read', [file, options], resolvers);
24
24
  return result;
25
25
  } catch (error) {
26
26
  throw new ResolveError(`Error while reading file "${file.uri}"`, {
@@ -344,8 +344,8 @@ export declare abstract class Parser {
344
344
  */
345
345
  mediaTypes: string[];
346
346
  constructor({ name, allowEmpty, sourceMap, strict, fileExtensions, mediaTypes, }: ParserOptions);
347
- abstract canParse(file: File_2): boolean | Promise<boolean>;
348
- abstract parse(file: File_2): ParseResultElement | Promise<ParseResultElement>;
347
+ abstract canParse(file: File_2, options?: ApiDOMReferenceOptions): boolean | Promise<boolean>;
348
+ abstract parse(file: File_2, options?: ApiDOMReferenceOptions): ParseResultElement | Promise<ParseResultElement>;
349
349
  }
350
350
 
351
351
  /**
@@ -457,8 +457,8 @@ export declare class ResolveError extends ApiDOMError {
457
457
  export declare abstract class Resolver {
458
458
  readonly name: string;
459
459
  constructor({ name }: ResolverOptions);
460
- abstract canRead(file: File_2): boolean;
461
- abstract read(file: File_2): Promise<Buffer>;
460
+ abstract canRead(file: File_2, options?: ApiDOMReferenceOptions): boolean;
461
+ abstract read(file: File_2, options?: ApiDOMReferenceOptions): Promise<Buffer>;
462
462
  }
463
463
 
464
464
  /**
@@ -533,6 +533,12 @@ export declare class UnmatchedBundleStrategyError extends BundleError {
533
533
  export declare class UnmatchedDereferenceStrategyError extends DereferenceError {
534
534
  }
535
535
 
536
+ /**
537
+ * @public
538
+ */
539
+ export declare class UnmatchedParserError extends ParserError {
540
+ }
541
+
536
542
  /**
537
543
  * @public
538
544
  */
@@ -0,0 +1,7 @@
1
+ import ParserError from './ParserError.ts';
2
+ /**
3
+ * @public
4
+ */
5
+ declare class UnmatchedParserError extends ParserError {
6
+ }
7
+ export default UnmatchedParserError;
package/types/index.d.ts CHANGED
@@ -45,6 +45,7 @@ export { default as ResolverError } from './errors/ResolverError.ts';
45
45
  export { default as UnmatchedDereferenceStrategyError } from './errors/UnmatchedDereferenceStrategyError.ts';
46
46
  export { default as UnmatchedResolveStrategyError } from './errors/UnmatchedResolveStrategyError.ts';
47
47
  export { default as UnmatchedResolverError } from './errors/UnmatchedResolverError.ts';
48
+ export { default as UnmatchedParserError } from './errors/UnmatchedParserError.ts';
48
49
  /**
49
50
  * @public
50
51
  */
@@ -1,5 +1,6 @@
1
1
  import { ParseResultElement } from '@speclynx/apidom-datamodel';
2
2
  import File from '../../File.ts';
3
+ import type { ReferenceOptions } from '../../options/index.ts';
3
4
  /**
4
5
  * @public
5
6
  */
@@ -37,7 +38,7 @@ declare abstract class Parser {
37
38
  */
38
39
  mediaTypes: string[];
39
40
  constructor({ name, allowEmpty, sourceMap, strict, fileExtensions, mediaTypes, }: ParserOptions);
40
- abstract canParse(file: File): boolean | Promise<boolean>;
41
- abstract parse(file: File): ParseResultElement | Promise<ParseResultElement>;
41
+ abstract canParse(file: File, options?: ReferenceOptions): boolean | Promise<boolean>;
42
+ abstract parse(file: File, options?: ReferenceOptions): ParseResultElement | Promise<ParseResultElement>;
42
43
  }
43
44
  export default Parser;
@@ -1,20 +1,24 @@
1
1
  import { ParseResultElement } from '@speclynx/apidom-datamodel';
2
2
  import Parser, { ParserOptions } from '../Parser.ts';
3
3
  import File from '../../../File.ts';
4
+ import type { ReferenceOptions } from '../../../options/index.ts';
5
+ import type ParseFn from '../../index.ts';
4
6
  export type { default as Parser, ParserOptions } from '../Parser.ts';
5
7
  export type { default as File, FileOptions } from '../../../File.ts';
6
8
  /**
7
9
  * @public
8
10
  */
9
11
  export interface ArazzoJSON1ParserOptions extends Omit<ParserOptions, 'name'> {
12
+ readonly parseFn?: typeof ParseFn;
10
13
  }
11
14
  /**
12
15
  * @public
13
16
  */
14
17
  declare class ArazzoJSON1Parser extends Parser {
15
18
  refractorOpts: object;
19
+ parseFn?: typeof ParseFn;
16
20
  constructor(options?: ArazzoJSON1ParserOptions);
17
21
  canParse(file: File): Promise<boolean>;
18
- parse(file: File): Promise<ParseResultElement>;
22
+ parse(file: File, options?: ReferenceOptions): Promise<ParseResultElement>;
19
23
  }
20
24
  export default ArazzoJSON1Parser;
@@ -0,0 +1,13 @@
1
+ import { Element, ParseResultElement } from '@speclynx/apidom-datamodel';
2
+ import File from '../../../File.ts';
3
+ import type { ReferenceOptions } from '../../../options/index.ts';
4
+ import type ParseFn from '../../index.ts';
5
+ /**
6
+ * Shared function for parsing source descriptions.
7
+ * Call with `.call(this, ...)` where `this` has `name` and `parseFn` properties.
8
+ * @public
9
+ */
10
+ export declare function parseSourceDescriptions(this: {
11
+ name: string;
12
+ parseFn?: typeof ParseFn;
13
+ }, api: Element | undefined, file: File, options: ReferenceOptions): Promise<ParseResultElement[]>;
@@ -1,20 +1,24 @@
1
1
  import { ParseResultElement } from '@speclynx/apidom-datamodel';
2
2
  import Parser, { ParserOptions } from '../Parser.ts';
3
3
  import File from '../../../File.ts';
4
+ import type { ReferenceOptions } from '../../../options/index.ts';
5
+ import type ParseFn from '../../index.ts';
4
6
  export type { default as Parser, ParserOptions } from '../Parser.ts';
5
7
  export type { default as File, FileOptions } from '../../../File.ts';
6
8
  /**
7
9
  * @public
8
10
  */
9
11
  export interface ArazzoYAML1ParserOptions extends Omit<ParserOptions, 'name'> {
12
+ readonly parseFn?: typeof ParseFn;
10
13
  }
11
14
  /**
12
15
  * @public
13
16
  */
14
17
  declare class ArazzoYAML1Parser extends Parser {
15
18
  refractorOpts: object;
19
+ parseFn?: typeof ParseFn;
16
20
  constructor(options?: ArazzoYAML1ParserOptions);
17
21
  canParse(file: File): Promise<boolean>;
18
- parse(file: File): Promise<ParseResultElement>;
22
+ parse(file: File, options?: ReferenceOptions): Promise<ParseResultElement>;
19
23
  }
20
24
  export default ArazzoYAML1Parser;
@@ -1,4 +1,5 @@
1
1
  import File from '../../File.ts';
2
+ import type { ReferenceOptions } from '../../options/index.ts';
2
3
  /**
3
4
  * @public
4
5
  */
@@ -11,7 +12,7 @@ export interface ResolverOptions {
11
12
  declare abstract class Resolver {
12
13
  readonly name: string;
13
14
  constructor({ name }: ResolverOptions);
14
- abstract canRead(file: File): boolean;
15
- abstract read(file: File): Promise<Buffer>;
15
+ abstract canRead(file: File, options?: ReferenceOptions): boolean;
16
+ abstract read(file: File, options?: ReferenceOptions): Promise<Buffer>;
16
17
  }
17
18
  export default Resolver;