@speclynx/apidom-reference 6.1.2 → 7.0.0

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 (106) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +57 -0
  3. package/dist/apidom-reference.browser.js +5783 -3741
  4. package/dist/apidom-reference.browser.min.js +1 -1
  5. package/package.json +31 -30
  6. package/src/File.cjs +5 -2
  7. package/src/File.mjs +5 -2
  8. package/src/bundle/index.cjs +6 -4
  9. package/src/bundle/index.mjs +6 -4
  10. package/src/bundle/strategies/arazzo-1/index.cjs +3 -2
  11. package/src/bundle/strategies/arazzo-1/index.mjs +3 -2
  12. package/src/bundle/strategies/asyncapi-2/index.cjs +3 -2
  13. package/src/bundle/strategies/asyncapi-2/index.mjs +3 -2
  14. package/src/bundle/strategies/openapi-2/index.cjs +3 -2
  15. package/src/bundle/strategies/openapi-2/index.mjs +3 -2
  16. package/src/bundle/strategies/openapi-3-0/index.cjs +3 -2
  17. package/src/bundle/strategies/openapi-3-0/index.mjs +3 -2
  18. package/src/bundle/strategies/openapi-3-1/index.cjs +3 -2
  19. package/src/bundle/strategies/openapi-3-1/index.mjs +3 -2
  20. package/src/dereference/index.cjs +8 -5
  21. package/src/dereference/index.mjs +8 -5
  22. package/src/dereference/strategies/DereferenceVisitor.cjs +588 -0
  23. package/src/dereference/strategies/DereferenceVisitor.mjs +580 -0
  24. package/src/dereference/strategies/arazzo-1/index.cjs +14 -39
  25. package/src/dereference/strategies/arazzo-1/index.mjs +15 -40
  26. package/src/dereference/strategies/arazzo-1/visitor.cjs +207 -331
  27. package/src/dereference/strategies/arazzo-1/visitor.mjs +215 -338
  28. package/src/dereference/strategies/asyncapi-2/index.cjs +11 -39
  29. package/src/dereference/strategies/asyncapi-2/index.mjs +11 -39
  30. package/src/dereference/strategies/asyncapi-2/visitor.cjs +133 -425
  31. package/src/dereference/strategies/asyncapi-2/visitor.mjs +137 -429
  32. package/src/dereference/strategies/openapi-2/index.cjs +12 -42
  33. package/src/dereference/strategies/openapi-2/index.mjs +12 -42
  34. package/src/dereference/strategies/openapi-2/visitor.cjs +168 -532
  35. package/src/dereference/strategies/openapi-2/visitor.mjs +172 -536
  36. package/src/dereference/strategies/openapi-3-0/index.cjs +12 -42
  37. package/src/dereference/strategies/openapi-3-0/index.mjs +12 -42
  38. package/src/dereference/strategies/openapi-3-0/visitor.cjs +194 -468
  39. package/src/dereference/strategies/openapi-3-0/visitor.mjs +199 -473
  40. package/src/dereference/strategies/openapi-3-1/index.cjs +16 -42
  41. package/src/dereference/strategies/openapi-3-1/index.mjs +16 -42
  42. package/src/dereference/strategies/openapi-3-1/visitor.cjs +350 -602
  43. package/src/dereference/strategies/openapi-3-1/visitor.mjs +357 -608
  44. package/src/dereference/strategies/overlay-1/index.cjs +3 -2
  45. package/src/dereference/strategies/overlay-1/index.mjs +3 -2
  46. package/src/index.cjs +0 -7
  47. package/src/index.mjs +0 -1
  48. package/src/options/index.cjs +4 -0
  49. package/src/options/index.mjs +4 -0
  50. package/src/options/util.cjs +7 -1
  51. package/src/options/util.mjs +8 -2
  52. package/src/parse/index.cjs +9 -1
  53. package/src/parse/index.mjs +9 -1
  54. package/src/parse/parsers/apidom-json/index.cjs +2 -1
  55. package/src/parse/parsers/apidom-json/index.mjs +2 -1
  56. package/src/parse/parsers/arazzo-json-1/index.cjs +2 -1
  57. package/src/parse/parsers/arazzo-json-1/index.mjs +2 -1
  58. package/src/parse/parsers/arazzo-yaml-1/index.cjs +2 -1
  59. package/src/parse/parsers/arazzo-yaml-1/index.mjs +2 -1
  60. package/src/parse/parsers/asyncapi-json-2/index.cjs +2 -1
  61. package/src/parse/parsers/asyncapi-json-2/index.mjs +2 -1
  62. package/src/parse/parsers/asyncapi-yaml-2/index.cjs +2 -1
  63. package/src/parse/parsers/asyncapi-yaml-2/index.mjs +2 -1
  64. package/src/parse/parsers/json/index.cjs +2 -1
  65. package/src/parse/parsers/json/index.mjs +2 -1
  66. package/src/parse/parsers/openapi-json-2/index.cjs +2 -1
  67. package/src/parse/parsers/openapi-json-2/index.mjs +2 -1
  68. package/src/parse/parsers/openapi-json-3-0/index.cjs +2 -1
  69. package/src/parse/parsers/openapi-json-3-0/index.mjs +2 -1
  70. package/src/parse/parsers/openapi-json-3-1/index.cjs +2 -1
  71. package/src/parse/parsers/openapi-json-3-1/index.mjs +2 -1
  72. package/src/parse/parsers/openapi-yaml-2/index.cjs +2 -1
  73. package/src/parse/parsers/openapi-yaml-2/index.mjs +2 -1
  74. package/src/parse/parsers/openapi-yaml-3-0/index.cjs +2 -1
  75. package/src/parse/parsers/openapi-yaml-3-0/index.mjs +2 -1
  76. package/src/parse/parsers/openapi-yaml-3-1/index.cjs +2 -1
  77. package/src/parse/parsers/openapi-yaml-3-1/index.mjs +2 -1
  78. package/src/parse/parsers/overlay-json-1/index.cjs +2 -1
  79. package/src/parse/parsers/overlay-json-1/index.mjs +2 -1
  80. package/src/parse/parsers/overlay-yaml-1/index.cjs +2 -1
  81. package/src/parse/parsers/overlay-yaml-1/index.mjs +2 -1
  82. package/src/parse/parsers/yaml-1-2/index.cjs +2 -1
  83. package/src/parse/parsers/yaml-1-2/index.mjs +2 -1
  84. package/src/resolve/index.cjs +6 -3
  85. package/src/resolve/index.mjs +6 -3
  86. package/src/util/url.cjs +38 -8
  87. package/src/util/url.mjs +35 -6
  88. package/types/apidom-reference.d.ts +13 -9
  89. package/types/dereference/strategies/DereferenceVisitor.d.ts +224 -0
  90. package/types/dereference/strategies/arazzo-1/index.d.ts +0 -1
  91. package/types/dereference/strategies/arazzo-1/visitor.d.ts +38 -38
  92. package/types/dereference/strategies/asyncapi-2/index.d.ts +0 -1
  93. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +13 -29
  94. package/types/dereference/strategies/openapi-2/index.d.ts +0 -1
  95. package/types/dereference/strategies/openapi-2/visitor.d.ts +14 -29
  96. package/types/dereference/strategies/openapi-3-0/index.d.ts +0 -1
  97. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +13 -28
  98. package/types/dereference/strategies/openapi-3-1/index.d.ts +0 -1
  99. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +37 -33
  100. package/types/index.d.ts +0 -1
  101. package/types/util/url.d.ts +11 -0
  102. package/LICENSES/AFL-3.0.txt +0 -182
  103. package/LICENSES/BSD-3-Clause.txt +0 -26
  104. package/src/dereference/util.cjs +0 -33
  105. package/src/dereference/util.mjs +0 -27
  106. package/types/dereference/util.d.ts +0 -9
@@ -1,5 +1,6 @@
1
1
  import { pick } from 'ramda';
2
2
  import { parse, mediaTypes as OverlayJSON1MediaTypes, detect } from '@speclynx/apidom-parser-adapter-overlay-json-1';
3
+ import { selectBestMatch } from '@swaggerexpert/media-types';
3
4
  import ParserError from "../../../errors/ParserError.mjs";
4
5
  import Parser from "../Parser.mjs";
5
6
  import { parseExtends } from "./extends.mjs";
@@ -29,7 +30,7 @@ class OverlayJSON1Parser extends Parser {
29
30
  }
30
31
  async canParse(file) {
31
32
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
32
- const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);
33
+ const hasSupportedMediaType = selectBestMatch(this.mediaTypes, file.mediaType) !== null;
33
34
  if (!hasSupportedFileExtension) return false;
34
35
  if (hasSupportedMediaType) return true;
35
36
  if (!hasSupportedMediaType) {
@@ -13,6 +13,7 @@ Object.defineProperty(exports, "parseExtends", {
13
13
  });
14
14
  var _ramda = require("ramda");
15
15
  var _apidomParserAdapterOverlayYaml = require("@speclynx/apidom-parser-adapter-overlay-yaml-1");
16
+ var _mediaTypes = require("@swaggerexpert/media-types");
16
17
  var _ParserError = _interopRequireDefault(require("../../../errors/ParserError.cjs"));
17
18
  var _Parser = _interopRequireDefault(require("../Parser.cjs"));
18
19
  var _extends = require("./extends.cjs");
@@ -43,7 +44,7 @@ class OverlayYAML1Parser extends _Parser.default {
43
44
  }
44
45
  async canParse(file) {
45
46
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
46
- const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);
47
+ const hasSupportedMediaType = (0, _mediaTypes.selectBestMatch)(this.mediaTypes, file.mediaType) !== null;
47
48
  if (!hasSupportedFileExtension) return false;
48
49
  if (hasSupportedMediaType) return true;
49
50
  if (!hasSupportedMediaType) {
@@ -1,5 +1,6 @@
1
1
  import { pick } from 'ramda';
2
2
  import { parse, mediaTypes as OverlayYAML1MediaTypes, detect } from '@speclynx/apidom-parser-adapter-overlay-yaml-1';
3
+ import { selectBestMatch } from '@swaggerexpert/media-types';
3
4
  import ParserError from "../../../errors/ParserError.mjs";
4
5
  import Parser from "../Parser.mjs";
5
6
  import { parseExtends } from "./extends.mjs";
@@ -29,7 +30,7 @@ class OverlayYAML1Parser extends Parser {
29
30
  }
30
31
  async canParse(file) {
31
32
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
32
- const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);
33
+ const hasSupportedMediaType = selectBestMatch(this.mediaTypes, file.mediaType) !== null;
33
34
  if (!hasSupportedFileExtension) return false;
34
35
  if (hasSupportedMediaType) return true;
35
36
  if (!hasSupportedMediaType) {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _apidomParserAdapterYaml = require("@speclynx/apidom-parser-adapter-yaml-1-2");
9
+ var _mediaTypes = require("@swaggerexpert/media-types");
9
10
  var _ParserError = _interopRequireDefault(require("../../../errors/ParserError.cjs"));
10
11
  var _Parser = _interopRequireDefault(require("../Parser.cjs"));
11
12
  /**
@@ -32,7 +33,7 @@ class YAML1Parser extends _Parser.default {
32
33
  }
33
34
  async canParse(file) {
34
35
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
35
- const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);
36
+ const hasSupportedMediaType = (0, _mediaTypes.selectBestMatch)(this.mediaTypes, file.mediaType) !== null;
36
37
  if (!hasSupportedFileExtension) return false;
37
38
  if (hasSupportedMediaType) return true;
38
39
  if (!hasSupportedMediaType) {
@@ -1,4 +1,5 @@
1
1
  import { parse, mediaTypes as YAMLMediaTypes, detect } from '@speclynx/apidom-parser-adapter-yaml-1-2';
2
+ import { selectBestMatch } from '@swaggerexpert/media-types';
2
3
  import ParserError from "../../../errors/ParserError.mjs";
3
4
  import Parser from "../Parser.mjs";
4
5
  /**
@@ -24,7 +25,7 @@ class YAML1Parser extends Parser {
24
25
  }
25
26
  async canParse(file) {
26
27
  const hasSupportedFileExtension = this.fileExtensions.length === 0 ? true : this.fileExtensions.includes(file.extension);
27
- const hasSupportedMediaType = this.mediaTypes.includes(file.mediaType);
28
+ const hasSupportedMediaType = selectBestMatch(this.mediaTypes, file.mediaType) !== null;
28
29
  if (!hasSupportedFileExtension) return false;
29
30
  if (hasSupportedMediaType) return true;
30
31
  if (!hasSupportedMediaType) {
@@ -29,9 +29,10 @@ const resolveApiDOM = async (element, options) => {
29
29
  elementClone.classes.push('result');
30
30
  parseResult = new _apidomDatamodel.ParseResultElement([elementClone]);
31
31
  }
32
- const sanitizedURI = url.sanitize(url.stripHash(options.resolve.baseURI));
32
+
33
+ // strategies look up same-document references by a resolved, hence normalized, URI
33
34
  const file = new _File.default({
34
- uri: sanitizedURI,
35
+ uri: url.normalize(options.resolve.baseURI),
35
36
  parseResult,
36
37
  mediaType: options.parse.mediaType
37
38
  });
@@ -59,9 +60,11 @@ const resolveApiDOM = async (element, options) => {
59
60
  exports.resolveApiDOM = resolveApiDOM;
60
61
  const resolve = async (uri, options) => {
61
62
  const parseResult = await (0, _index.default)(uri, options);
63
+ // the file is parsed as a whole document, so its URI has no fragment
64
+ const baseURI = url.normalize(url.stripHash(uri));
62
65
  const mergedOptions = (0, _util.merge)(options, {
63
66
  resolve: {
64
- baseURI: url.sanitize(uri)
67
+ baseURI
65
68
  }
66
69
  });
67
70
  return resolveApiDOM(parseResult, mergedOptions);
@@ -21,9 +21,10 @@ export const resolveApiDOM = async (element, options) => {
21
21
  elementClone.classes.push('result');
22
22
  parseResult = new ParseResultElement([elementClone]);
23
23
  }
24
- const sanitizedURI = url.sanitize(url.stripHash(options.resolve.baseURI));
24
+
25
+ // strategies look up same-document references by a resolved, hence normalized, URI
25
26
  const file = new File({
26
- uri: sanitizedURI,
27
+ uri: url.normalize(options.resolve.baseURI),
27
28
  parseResult,
28
29
  mediaType: options.parse.mediaType
29
30
  });
@@ -50,9 +51,11 @@ export const resolveApiDOM = async (element, options) => {
50
51
  */
51
52
  const resolve = async (uri, options) => {
52
53
  const parseResult = await parse(uri, options);
54
+ // the file is parsed as a whole document, so its URI has no fragment
55
+ const baseURI = url.normalize(url.stripHash(uri));
53
56
  const mergedOptions = mergeOptions(options, {
54
57
  resolve: {
55
- baseURI: url.sanitize(uri)
58
+ baseURI
56
59
  }
57
60
  });
58
61
  return resolveApiDOM(parseResult, mergedOptions);
package/src/util/url.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.relative = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
6
+ exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.relative = exports.normalize = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
7
7
  var _ramda = require("ramda");
8
8
  var _ramdaAdjunct = require("ramda-adjunct");
9
9
  /**
@@ -118,21 +118,23 @@ exports.isURI = isURI;
118
118
  */
119
119
  const toFileSystemPath = (uri, options) => {
120
120
  // RegExp patterns to URL-decode special characters for local filesystem paths
121
- const urlDecodePatterns = [/%23/g, '#', /%24/g, '$', /%26/g, '&', /%2C/g, ',', /%40/g, '@'];
121
+ const urlDecodePatterns = [/%23/g, '#', /%24/g, '$', /%26/g, '&', /%2C/g, ',', /%3F/g, '?', /%40/g, '@'];
122
122
  const keepFileProtocol = (0, _ramda.propOr)(false, 'keepFileProtocol', options);
123
123
  const isWindowsPredicate = (0, _ramda.propOr)(isWindows, 'isWindows', options);
124
124
 
125
- // Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
126
- let path = decodeURI(uri);
127
-
128
- // Step 2: Manually decode characters that are not decoded by `decodeURI`.
125
+ // Step 1: Manually decode characters that are not decoded by `decodeURI`.
129
126
  // This includes characters such as "#" and "?", which have special meaning in URLs,
130
- // but are just normal characters in a filesystem path.
127
+ // but are just normal characters in a filesystem path. It runs before `decodeURI`,
128
+ // so that an encoded "%" (e.g. "%253F") isn't decoded twice.
129
+ let path = uri;
131
130
  for (let i = 0; i < urlDecodePatterns.length; i += 2) {
132
131
  // @ts-ignore
133
132
  path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1]);
134
133
  }
135
134
 
135
+ // Step 2: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
136
+ path = decodeURI(path);
137
+
136
138
  // Step 3: If it's a "file://" URL, then format it consistently
137
139
  // or convert it to a local filesystem path
138
140
  let isFileUrl = path.substring(0, 7).toLowerCase() === 'file://';
@@ -278,6 +280,34 @@ const resolve = (from, to) => {
278
280
  return resolvedUrl.toString();
279
281
  };
280
282
 
283
+ /**
284
+ * Normalizes a URI the way `resolve` normalizes its result (WHATWG URL normalization):
285
+ * lowercases the scheme and the host of an absolute URL, drops a default port and
286
+ * removes dot segments. This is not RFC 3986 syntax-based normalization: existing
287
+ * percent-encoding is left as is. A file system path is sanitized first, so characters
288
+ * not allowed in URLs (e.g. spaces, `[`, `?`) are percent-encoded; a `file://` URL has
289
+ * its path sanitized the same way and stays a URL. The fragment is kept as given.
290
+ * A URI that can't be normalized is returned unchanged.
291
+ * @public
292
+ */
293
+ exports.resolve = resolve;
294
+ const normalize = uri => {
295
+ // getHash returns the root hash ('#') for a URI without fragment
296
+ const fragment = uri.includes('#') ? getHash(uri) : '';
297
+ const documentURI = stripHash(uri);
298
+ try {
299
+ let sanitizedURI = sanitize(documentURI);
300
+ // sanitize turns a file:// URL into a path; keep it a URL, as references
301
+ // resolved against it are, e.g. file:///C:/x.json rather than file://C:/x.json
302
+ if (getProtocol(documentURI) === 'file') {
303
+ sanitizedURI = `file://${sanitizedURI.startsWith('/') ? '' : '/'}${sanitizedURI}`;
304
+ }
305
+ return resolve('', sanitizedURI) + fragment;
306
+ } catch {
307
+ return uri;
308
+ }
309
+ };
310
+
281
311
  /**
282
312
  * Computes the URI reference that resolves to `to` against `from` — the inverse
283
313
  * of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
@@ -287,7 +317,7 @@ const resolve = (from, to) => {
287
317
  * returned unchanged.
288
318
  * @public
289
319
  */
290
- exports.resolve = resolve;
320
+ exports.normalize = normalize;
291
321
  const relative = (from, to) => {
292
322
  const fromURL = new URL(from, 'resolve://');
293
323
  const toURL = new URL(to, 'resolve://');
package/src/util/url.mjs CHANGED
@@ -108,21 +108,23 @@ export const isURI = uri => {
108
108
  */
109
109
  export const toFileSystemPath = (uri, options) => {
110
110
  // RegExp patterns to URL-decode special characters for local filesystem paths
111
- const urlDecodePatterns = [/%23/g, '#', /%24/g, '$', /%26/g, '&', /%2C/g, ',', /%40/g, '@'];
111
+ const urlDecodePatterns = [/%23/g, '#', /%24/g, '$', /%26/g, '&', /%2C/g, ',', /%3F/g, '?', /%40/g, '@'];
112
112
  const keepFileProtocol = propOr(false, 'keepFileProtocol', options);
113
113
  const isWindowsPredicate = propOr(isWindows, 'isWindows', options);
114
114
 
115
- // Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
116
- let path = decodeURI(uri);
117
-
118
- // Step 2: Manually decode characters that are not decoded by `decodeURI`.
115
+ // Step 1: Manually decode characters that are not decoded by `decodeURI`.
119
116
  // This includes characters such as "#" and "?", which have special meaning in URLs,
120
- // but are just normal characters in a filesystem path.
117
+ // but are just normal characters in a filesystem path. It runs before `decodeURI`,
118
+ // so that an encoded "%" (e.g. "%253F") isn't decoded twice.
119
+ let path = uri;
121
120
  for (let i = 0; i < urlDecodePatterns.length; i += 2) {
122
121
  // @ts-ignore
123
122
  path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1]);
124
123
  }
125
124
 
125
+ // Step 2: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
126
+ path = decodeURI(path);
127
+
126
128
  // Step 3: If it's a "file://" URL, then format it consistently
127
129
  // or convert it to a local filesystem path
128
130
  let isFileUrl = path.substring(0, 7).toLowerCase() === 'file://';
@@ -263,6 +265,33 @@ export const resolve = (from, to) => {
263
265
  return resolvedUrl.toString();
264
266
  };
265
267
 
268
+ /**
269
+ * Normalizes a URI the way `resolve` normalizes its result (WHATWG URL normalization):
270
+ * lowercases the scheme and the host of an absolute URL, drops a default port and
271
+ * removes dot segments. This is not RFC 3986 syntax-based normalization: existing
272
+ * percent-encoding is left as is. A file system path is sanitized first, so characters
273
+ * not allowed in URLs (e.g. spaces, `[`, `?`) are percent-encoded; a `file://` URL has
274
+ * its path sanitized the same way and stays a URL. The fragment is kept as given.
275
+ * A URI that can't be normalized is returned unchanged.
276
+ * @public
277
+ */
278
+ export const normalize = uri => {
279
+ // getHash returns the root hash ('#') for a URI without fragment
280
+ const fragment = uri.includes('#') ? getHash(uri) : '';
281
+ const documentURI = stripHash(uri);
282
+ try {
283
+ let sanitizedURI = sanitize(documentURI);
284
+ // sanitize turns a file:// URL into a path; keep it a URL, as references
285
+ // resolved against it are, e.g. file:///C:/x.json rather than file://C:/x.json
286
+ if (getProtocol(documentURI) === 'file') {
287
+ sanitizedURI = `file://${sanitizedURI.startsWith('/') ? '' : '/'}${sanitizedURI}`;
288
+ }
289
+ return resolve('', sanitizedURI) + fragment;
290
+ } catch {
291
+ return uri;
292
+ }
293
+ };
294
+
266
295
  /**
267
296
  * Computes the URI reference that resolves to `to` against `from` — the inverse
268
297
  * of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
@@ -143,15 +143,6 @@ declare const cwd: () => string;
143
143
  */
144
144
  export declare const dereference: (uri: string, options?: {}) => Promise<ParseResultElement>;
145
145
 
146
- /**
147
- * @public
148
- */
149
- export declare class DereferenceAncestorLineage<T extends Element_2> extends Array<Set<T>> {
150
- includesCycle(element: T): boolean;
151
- includes(searchElement: Set<T> | T, fromIndex?: number): boolean;
152
- findItem(predicate: (item: T) => boolean): T | undefined;
153
- }
154
-
155
146
  /**
156
147
  * @public
157
148
  */
@@ -359,6 +350,18 @@ export declare class MaximumResolveDepthError extends ResolveError {
359
350
  */
360
351
  export declare const mergeOptions: (lObj: ApiDOMReferenceOptions, rObj: Record<string, any>) => ApiDOMReferenceOptions;
361
352
 
353
+ /**
354
+ * Normalizes a URI the way `resolve` normalizes its result (WHATWG URL normalization):
355
+ * lowercases the scheme and the host of an absolute URL, drops a default port and
356
+ * removes dot segments. This is not RFC 3986 syntax-based normalization: existing
357
+ * percent-encoding is left as is. A file system path is sanitized first, so characters
358
+ * not allowed in URLs (e.g. spaces, `[`, `?`) are percent-encoded; a `file://` URL has
359
+ * its path sanitized the same way and stays a URL. The fragment is kept as given.
360
+ * A URI that can't be normalized is returned unchanged.
361
+ * @public
362
+ */
363
+ declare const normalize: (uri: string) => string;
364
+
362
365
  /**
363
366
  * @public
364
367
  */
@@ -658,6 +661,7 @@ declare namespace url {
658
661
  stripHash,
659
662
  cwd,
660
663
  resolve_2 as resolve,
664
+ normalize,
661
665
  relative,
662
666
  sanitize,
663
667
  unsanitize
@@ -0,0 +1,224 @@
1
+ import { Element } from '@speclynx/apidom-datamodel';
2
+ import { type Path } from '@speclynx/apidom-traverse';
3
+ import Reference from '../../Reference.ts';
4
+ import type { ReferenceOptions } from '../../options/index.ts';
5
+ /**
6
+ * Whether the element is generic ApiDOM: an instance of a primitive element class itself.
7
+ * Semantic elements may carry a primitive element name (e.g. a map of components named
8
+ * `object`), so the name alone doesn't tell.
9
+ * @internal
10
+ */
11
+ export declare const isGenericElement: (element: Element) => boolean;
12
+ /**
13
+ * A source dereferenced as one type: the source as that semantic type, with every reference
14
+ * within it transcluded. `element` exists from the start, so references leading back to
15
+ * an element still being dereferenced (cycles) can be transcluded with it.
16
+ * @internal
17
+ */
18
+ export interface DereferencedElement {
19
+ element: Element;
20
+ done: boolean;
21
+ height: number;
22
+ alias: boolean;
23
+ unresolved: boolean;
24
+ }
25
+ /**
26
+ * How a referencing element is transcluded with the dereferenced target of its reference.
27
+ * @internal
28
+ */
29
+ export interface TransclusionSpec {
30
+ /**
31
+ * The element transcluded in place of the referencing element, built from the dereferenced
32
+ * target (which may still be being dereferenced when the reference leads back to it).
33
+ */
34
+ readonly toTranscludedElement: (referencingElement: Element, target: Element) => Element;
35
+ /**
36
+ * Field holding the reference of an alias, for references merging their target with
37
+ * the fields next to them (e.g. `$ref`): a target that is an alias whose reference never
38
+ * reaches a value is merged without that field, so only the fields along the chain remain.
39
+ * Without it, a referencing element leading to such an alias is kept as written.
40
+ */
41
+ readonly aliasField?: string;
42
+ /**
43
+ * What a circular reference is reported and replaced with: the RefElement content and meta
44
+ * for `dereference.circular: 'replace'`, the error details for `'error'`.
45
+ */
46
+ readonly circular: {
47
+ readonly uri: string;
48
+ readonly meta: Record<string, unknown>;
49
+ readonly details: Record<string, unknown>;
50
+ };
51
+ }
52
+ /**
53
+ * A transclusion to perform.
54
+ * @internal
55
+ */
56
+ export interface Transclusion extends TransclusionSpec {
57
+ readonly target: DereferencedElement;
58
+ readonly back: boolean;
59
+ readonly keep: boolean;
60
+ readonly attachment: boolean;
61
+ }
62
+ /**
63
+ * The target of a reference: the source element, the type the reference expects, and the
64
+ * document the source belongs to. `context` is passed to the visitor dereferencing the source.
65
+ * @internal
66
+ */
67
+ export interface DereferenceTarget<TContext = unknown> {
68
+ readonly source: Element;
69
+ readonly type: string;
70
+ readonly reference: Reference;
71
+ readonly context: TContext;
72
+ }
73
+ /**
74
+ * State shared by all the visitors of a single dereference run.
75
+ * @internal
76
+ */
77
+ export interface DereferenceRun {
78
+ readonly dereferenced: WeakMap<Element, Map<string, DereferencedElement>>;
79
+ readonly refracted: WeakMap<Element, Map<string, Element>>;
80
+ readonly inPlace: boolean;
81
+ readonly copies: WeakMap<object, object>;
82
+ readonly transcluded: WeakSet<Element>;
83
+ readonly failed: WeakSet<Element>;
84
+ readonly reported: Set<string>;
85
+ readonly undo: (() => void)[];
86
+ }
87
+ /**
88
+ * @internal
89
+ */
90
+ export interface DereferenceVisitorOptions {
91
+ readonly reference: Reference;
92
+ readonly options: ReferenceOptions;
93
+ }
94
+ /**
95
+ * Dereferences a document by the type each reference expects of its target. With the immutable
96
+ * option the documents aren't edited and the result is a new tree; otherwise they're
97
+ * dereferenced in place.
98
+ *
99
+ * Every referenced structure is dereferenced once per type the references reaching it expect.
100
+ * A source is dereferenced as a type in two steps. First it's traversed, and for each reference
101
+ * within it the target is located, dereferenced as the expected type, and recorded as
102
+ * a transclusion. Then the referencing elements are transcluded with the dereferenced targets.
103
+ * A dereferenced element is shared by all the references leading to it; a reference leading
104
+ * to an element still being dereferenced is a cycle.
105
+ *
106
+ * Strategies provide the handlers locating the targets of their references, the refraction to
107
+ * their semantic types and the visitors dereferencing the targets.
108
+ * @internal
109
+ */
110
+ declare abstract class DereferenceVisitor<TContext = unknown> {
111
+ protected readonly reference: Reference;
112
+ protected readonly options: ReferenceOptions;
113
+ /**
114
+ * Base URI of the current document for resolving relative references.
115
+ */
116
+ protected readonly baseURI: string;
117
+ protected run: DereferenceRun;
118
+ /**
119
+ * Map from the elements of the source the visitor traverses to their copies.
120
+ */
121
+ protected cloneMap?: WeakMap<object, object>;
122
+ /**
123
+ * Source the visitor traverses a refraction of, when it's dereferenced as another type.
124
+ */
125
+ protected refractedFrom?: Element;
126
+ /**
127
+ * Transclusions to perform, keyed by the (copy of the) referencing element.
128
+ */
129
+ protected readonly transclusions: Map<Element, Transclusion>;
130
+ /**
131
+ * Number of references followed to reach the source the visitor traverses.
132
+ */
133
+ protected depth: number;
134
+ /**
135
+ * The source the visitor traverses, dereferenced.
136
+ */
137
+ protected current?: DereferencedElement;
138
+ constructor({ reference, options }: DereferenceVisitorOptions);
139
+ /**
140
+ * Name of the strategy, for strategy specific options.
141
+ */
142
+ protected abstract get strategyName(): string;
143
+ /**
144
+ * Creates the visitor dereferencing a source of the given document.
145
+ */
146
+ protected abstract createVisitor(reference: Reference, context: TContext): DereferenceVisitor;
147
+ /**
148
+ * Refracts a generic element to the given type.
149
+ */
150
+ protected abstract refractTo(element: Element, type: string): Element;
151
+ /**
152
+ * Whether the element is of the given type, so it needs no refraction.
153
+ */
154
+ protected isOfType(element: Element, type: string): boolean;
155
+ /**
156
+ * Base URI of a document: its retrieval URI unless the document identifies itself otherwise.
157
+ */
158
+ protected toDocumentBaseURI(reference: Reference): string;
159
+ /**
160
+ * A document already loaded in the run identified by the URI other than by its retrieval URI.
161
+ */
162
+ protected findReferenceByIdentity(_uri: string): Reference | undefined;
163
+ /**
164
+ * Dereferences the element, which belongs to the document of the visitor's reference.
165
+ * With the immutable option the element isn't edited and its dereferenced copy is returned;
166
+ * otherwise it's dereferenced in place and returned.
167
+ */
168
+ dereference<T extends Element>(element: T, context: TContext): Promise<T>;
169
+ protected toBaseURI(uri: string): string;
170
+ protected toReference(uri: string): Promise<Reference>;
171
+ /**
172
+ * Handles an error according to the continueOnError option.
173
+ *
174
+ * For new errors: wraps in UnresolvableReferenceError with structured context
175
+ * (type, uri, location, codeFrame, refFieldName, refFieldValue, trace).
176
+ * For errors already wrapped by a nested visitor: prepends the current hop to the trace.
177
+ *
178
+ * Inner/intermediate visitors always throw to let the trace accumulate.
179
+ * Only the entry document visitor respects continueOnError (callback/swallow/throw),
180
+ * and reports the failure of a referencing element once.
181
+ */
182
+ protected handleError(message: string, error: Error, referencingElement: Element, refFieldName: string, refFieldValue: string, visitorPath: Path<Element>): void;
183
+ /**
184
+ * The source as the given semantic type: the source itself when it's of that type, otherwise
185
+ * its refraction to that type, made once per dereference run. The refraction is made from
186
+ * the source dereferenced as its own type, so it doesn't depend on which reference
187
+ * reaches the source first.
188
+ */
189
+ protected toSemantic(target: DereferenceTarget<TContext>, depth: number): Promise<Element>;
190
+ /**
191
+ * Dereferences the source as the given type, once per dereference run. `depth` is the number
192
+ * of references followed to reach the source.
193
+ */
194
+ protected dereferenceAs(target: DereferenceTarget<TContext>, depth: number): Promise<DereferencedElement>;
195
+ /**
196
+ * Dereferences the target of a reference as the expected type and records the transclusion.
197
+ */
198
+ protected addTransclusion(path: Path<Element>, target: DereferenceTarget<TContext>, spec: TransclusionSpec): Promise<void>;
199
+ /**
200
+ * Records the transclusion of a referencing element with a target that isn't dereferenced:
201
+ * the target as the expected type is attached as it is, e.g. an Operation Object to the Link
202
+ * Object pointing at it. Attached targets never form cycles.
203
+ */
204
+ protected addAttachment(path: Path<Element>, target: DereferenceTarget<TContext>, toTranscludedElement: TransclusionSpec['toTranscludedElement']): Promise<void>;
205
+ /**
206
+ * Transcludes every referencing element within the element with its dereferenced target.
207
+ */
208
+ protected transclude(element: Element): Element;
209
+ /**
210
+ * The element transcluded in place of a referencing element.
211
+ */
212
+ protected toTranscludedElement(referencingElement: Element, transclusion: Transclusion): Element;
213
+ /**
214
+ * Whether the element is transcluded in place of a referencing element, in this run or,
215
+ * when dereferencing in place, in an earlier one: transcluded elements carry the `ref-type` meta,
216
+ * which only dereferencing sets (bundling annotates hoisted elements with `ref-origin` only).
217
+ */
218
+ protected isTranscluded(element: Element): boolean;
219
+ /**
220
+ * Dereferenced in place, a source may already hold transcluded elements.
221
+ */
222
+ enter(path: Path<Element>): void;
223
+ }
224
+ export default DereferenceVisitor;
@@ -13,7 +13,6 @@ export type { default as Parser, ParserOptions } from '../../../parse/parsers/Pa
13
13
  export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
14
14
  export type { default as ResolveStrategy, ResolveStrategyOptions, } from '../../../resolve/strategies/ResolveStrategy.ts';
15
15
  export type { default as BundleStrategy, BundleStrategyOptions, } from '../../../bundle/strategies/BundleStrategy.ts';
16
- export type { AncestorLineage } from '../../util.ts';
17
16
  /**
18
17
  * @public
19
18
  */