@speclynx/apidom-reference 1.12.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 (263) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +202 -0
  3. package/LICENSES/AFL-3.0.txt +182 -0
  4. package/LICENSES/Apache-2.0.txt +202 -0
  5. package/LICENSES/BSD-3-Clause.txt +26 -0
  6. package/LICENSES/MIT.txt +9 -0
  7. package/NOTICE +65 -0
  8. package/README.md +2107 -0
  9. package/dist/167.apidom-reference.browser.js +10 -0
  10. package/dist/167.apidom-reference.browser.min.js +1 -0
  11. package/dist/451.apidom-reference.browser.js +10 -0
  12. package/dist/451.apidom-reference.browser.min.js +1 -0
  13. package/dist/9786785aaddf11f37840fad896531940.wasm +0 -0
  14. package/dist/apidom-reference.browser.js +85376 -0
  15. package/dist/apidom-reference.browser.min.js +1 -0
  16. package/package.json +304 -0
  17. package/src/File.cjs +50 -0
  18. package/src/File.mjs +44 -0
  19. package/src/Reference.cjs +31 -0
  20. package/src/Reference.mjs +27 -0
  21. package/src/ReferenceSet.cjs +60 -0
  22. package/src/ReferenceSet.mjs +57 -0
  23. package/src/bundle/index.cjs +61 -0
  24. package/src/bundle/index.mjs +55 -0
  25. package/src/bundle/strategies/BundleStrategy.cjs +20 -0
  26. package/src/bundle/strategies/BundleStrategy.mjs +16 -0
  27. package/src/bundle/strategies/openapi-3-1/index.cjs +35 -0
  28. package/src/bundle/strategies/openapi-3-1/index.mjs +29 -0
  29. package/src/configuration/empty.cjs +9 -0
  30. package/src/configuration/empty.mjs +1 -0
  31. package/src/configuration/saturated.cjs +95 -0
  32. package/src/configuration/saturated.mjs +87 -0
  33. package/src/dereference/index.cjs +86 -0
  34. package/src/dereference/index.mjs +79 -0
  35. package/src/dereference/strategies/DereferenceStrategy.cjs +20 -0
  36. package/src/dereference/strategies/DereferenceStrategy.mjs +16 -0
  37. package/src/dereference/strategies/apidom/index.cjs +89 -0
  38. package/src/dereference/strategies/apidom/index.mjs +84 -0
  39. package/src/dereference/strategies/apidom/selectors/element-id.cjs +36 -0
  40. package/src/dereference/strategies/apidom/selectors/element-id.mjs +30 -0
  41. package/src/dereference/strategies/apidom/visitor.cjs +165 -0
  42. package/src/dereference/strategies/apidom/visitor.mjs +159 -0
  43. package/src/dereference/strategies/asyncapi-2/index.cjs +100 -0
  44. package/src/dereference/strategies/asyncapi-2/index.mjs +94 -0
  45. package/src/dereference/strategies/asyncapi-2/visitor.cjs +412 -0
  46. package/src/dereference/strategies/asyncapi-2/visitor.mjs +406 -0
  47. package/src/dereference/strategies/openapi-2/index.cjs +102 -0
  48. package/src/dereference/strategies/openapi-2/index.mjs +96 -0
  49. package/src/dereference/strategies/openapi-2/visitor.cjs +530 -0
  50. package/src/dereference/strategies/openapi-2/visitor.mjs +524 -0
  51. package/src/dereference/strategies/openapi-3-0/index.cjs +102 -0
  52. package/src/dereference/strategies/openapi-3-0/index.mjs +96 -0
  53. package/src/dereference/strategies/openapi-3-0/visitor.cjs +519 -0
  54. package/src/dereference/strategies/openapi-3-0/visitor.mjs +513 -0
  55. package/src/dereference/strategies/openapi-3-1/index.cjs +105 -0
  56. package/src/dereference/strategies/openapi-3-1/index.mjs +96 -0
  57. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +66 -0
  58. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +55 -0
  59. package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +50 -0
  60. package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +42 -0
  61. package/src/dereference/strategies/openapi-3-1/util.cjs +67 -0
  62. package/src/dereference/strategies/openapi-3-1/util.mjs +58 -0
  63. package/src/dereference/strategies/openapi-3-1/visitor.cjs +776 -0
  64. package/src/dereference/strategies/openapi-3-1/visitor.mjs +770 -0
  65. package/src/dereference/util.cjs +31 -0
  66. package/src/dereference/util.mjs +27 -0
  67. package/src/errors/BundleError.cjs +10 -0
  68. package/src/errors/BundleError.mjs +7 -0
  69. package/src/errors/DereferenceError.cjs +10 -0
  70. package/src/errors/DereferenceError.mjs +7 -0
  71. package/src/errors/EvaluationElementIdError.cjs +10 -0
  72. package/src/errors/EvaluationElementIdError.mjs +7 -0
  73. package/src/errors/EvaluationJsonSchema$anchorError.cjs +11 -0
  74. package/src/errors/EvaluationJsonSchema$anchorError.mjs +6 -0
  75. package/src/errors/EvaluationJsonSchemaUriError.cjs +11 -0
  76. package/src/errors/EvaluationJsonSchemaUriError.mjs +6 -0
  77. package/src/errors/InvalidJsonSchema$anchorError.cjs +15 -0
  78. package/src/errors/InvalidJsonSchema$anchorError.mjs +10 -0
  79. package/src/errors/JsonSchema$anchorError.cjs +10 -0
  80. package/src/errors/JsonSchema$anchorError.mjs +7 -0
  81. package/src/errors/JsonSchemaUriError.cjs +10 -0
  82. package/src/errors/JsonSchemaUriError.mjs +7 -0
  83. package/src/errors/MaximumBundleDepthError.cjs +11 -0
  84. package/src/errors/MaximumBundleDepthError.mjs +6 -0
  85. package/src/errors/MaximumDereferenceDepthError.cjs +11 -0
  86. package/src/errors/MaximumDereferenceDepthError.mjs +6 -0
  87. package/src/errors/MaximumResolveDepthError.cjs +11 -0
  88. package/src/errors/MaximumResolveDepthError.mjs +6 -0
  89. package/src/errors/ParseError.cjs +10 -0
  90. package/src/errors/ParseError.mjs +7 -0
  91. package/src/errors/ParserError.cjs +11 -0
  92. package/src/errors/ParserError.mjs +6 -0
  93. package/src/errors/PluginError.cjs +18 -0
  94. package/src/errors/PluginError.mjs +15 -0
  95. package/src/errors/ResolveError.cjs +10 -0
  96. package/src/errors/ResolveError.mjs +7 -0
  97. package/src/errors/ResolverError.cjs +11 -0
  98. package/src/errors/ResolverError.mjs +6 -0
  99. package/src/errors/UnmatchedBundleStrategyError.cjs +11 -0
  100. package/src/errors/UnmatchedBundleStrategyError.mjs +6 -0
  101. package/src/errors/UnmatchedDereferenceStrategyError.cjs +11 -0
  102. package/src/errors/UnmatchedDereferenceStrategyError.mjs +6 -0
  103. package/src/errors/UnmatchedResolveStrategyError.cjs +11 -0
  104. package/src/errors/UnmatchedResolveStrategyError.mjs +6 -0
  105. package/src/errors/UnmatchedResolverError.cjs +11 -0
  106. package/src/errors/UnmatchedResolverError.mjs +6 -0
  107. package/src/index.cjs +142 -0
  108. package/src/index.mjs +101 -0
  109. package/src/options/index.cjs +185 -0
  110. package/src/options/index.mjs +182 -0
  111. package/src/options/util.cjs +24 -0
  112. package/src/options/util.mjs +19 -0
  113. package/src/parse/index.cjs +69 -0
  114. package/src/parse/index.mjs +63 -0
  115. package/src/parse/parsers/Parser.cjs +48 -0
  116. package/src/parse/parsers/Parser.mjs +44 -0
  117. package/src/parse/parsers/api-design-systems-json/index.cjs +55 -0
  118. package/src/parse/parsers/api-design-systems-json/index.mjs +49 -0
  119. package/src/parse/parsers/api-design-systems-yaml/index.cjs +54 -0
  120. package/src/parse/parsers/api-design-systems-yaml/index.mjs +48 -0
  121. package/src/parse/parsers/apidom-json/index.cjs +70 -0
  122. package/src/parse/parsers/apidom-json/index.mjs +64 -0
  123. package/src/parse/parsers/arazzo-json-1/index.cjs +55 -0
  124. package/src/parse/parsers/arazzo-json-1/index.mjs +49 -0
  125. package/src/parse/parsers/arazzo-yaml-1/index.cjs +54 -0
  126. package/src/parse/parsers/arazzo-yaml-1/index.mjs +48 -0
  127. package/src/parse/parsers/asyncapi-json-2/index.cjs +55 -0
  128. package/src/parse/parsers/asyncapi-json-2/index.mjs +49 -0
  129. package/src/parse/parsers/asyncapi-yaml-2/index.cjs +54 -0
  130. package/src/parse/parsers/asyncapi-yaml-2/index.mjs +48 -0
  131. package/src/parse/parsers/binary/index-browser.cjs +56 -0
  132. package/src/parse/parsers/binary/index-browser.mjs +50 -0
  133. package/src/parse/parsers/binary/index-node.cjs +51 -0
  134. package/src/parse/parsers/binary/index-node.mjs +45 -0
  135. package/src/parse/parsers/json/index.cjs +54 -0
  136. package/src/parse/parsers/json/index.mjs +48 -0
  137. package/src/parse/parsers/openapi-json-2/index.cjs +55 -0
  138. package/src/parse/parsers/openapi-json-2/index.mjs +49 -0
  139. package/src/parse/parsers/openapi-json-3-0/index.cjs +55 -0
  140. package/src/parse/parsers/openapi-json-3-0/index.mjs +49 -0
  141. package/src/parse/parsers/openapi-json-3-1/index.cjs +55 -0
  142. package/src/parse/parsers/openapi-json-3-1/index.mjs +49 -0
  143. package/src/parse/parsers/openapi-yaml-2/index.cjs +54 -0
  144. package/src/parse/parsers/openapi-yaml-2/index.mjs +48 -0
  145. package/src/parse/parsers/openapi-yaml-3-0/index.cjs +54 -0
  146. package/src/parse/parsers/openapi-yaml-3-0/index.mjs +48 -0
  147. package/src/parse/parsers/openapi-yaml-3-1/index.cjs +54 -0
  148. package/src/parse/parsers/openapi-yaml-3-1/index.mjs +48 -0
  149. package/src/parse/parsers/yaml-1-2/index.cjs +54 -0
  150. package/src/parse/parsers/yaml-1-2/index.mjs +48 -0
  151. package/src/resolve/index.cjs +67 -0
  152. package/src/resolve/index.mjs +60 -0
  153. package/src/resolve/resolvers/HTTPResolver.cjs +38 -0
  154. package/src/resolve/resolvers/HTTPResolver.mjs +31 -0
  155. package/src/resolve/resolvers/Resolver.cjs +20 -0
  156. package/src/resolve/resolvers/Resolver.mjs +16 -0
  157. package/src/resolve/resolvers/file/index-browser.cjs +24 -0
  158. package/src/resolve/resolvers/file/index-browser.mjs +19 -0
  159. package/src/resolve/resolvers/file/index-node.cjs +49 -0
  160. package/src/resolve/resolvers/file/index-node.mjs +42 -0
  161. package/src/resolve/resolvers/http-axios/index.cjs +80 -0
  162. package/src/resolve/resolvers/http-axios/index.mjs +73 -0
  163. package/src/resolve/strategies/ResolveStrategy.cjs +20 -0
  164. package/src/resolve/strategies/ResolveStrategy.mjs +16 -0
  165. package/src/resolve/strategies/apidom/index.cjs +49 -0
  166. package/src/resolve/strategies/apidom/index.mjs +43 -0
  167. package/src/resolve/strategies/asyncapi-2/index.cjs +49 -0
  168. package/src/resolve/strategies/asyncapi-2/index.mjs +43 -0
  169. package/src/resolve/strategies/openapi-2/index.cjs +49 -0
  170. package/src/resolve/strategies/openapi-2/index.mjs +43 -0
  171. package/src/resolve/strategies/openapi-3-0/index.cjs +49 -0
  172. package/src/resolve/strategies/openapi-3-0/index.mjs +43 -0
  173. package/src/resolve/strategies/openapi-3-1/index.cjs +49 -0
  174. package/src/resolve/strategies/openapi-3-1/index.mjs +43 -0
  175. package/src/resolve/util.cjs +37 -0
  176. package/src/resolve/util.mjs +30 -0
  177. package/src/util/plugins.cjs +44 -0
  178. package/src/util/plugins.mjs +37 -0
  179. package/src/util/url.cjs +288 -0
  180. package/src/util/url.mjs +274 -0
  181. package/types/File.d.ts +24 -0
  182. package/types/Reference.d.ts +23 -0
  183. package/types/ReferenceSet.d.ts +25 -0
  184. package/types/apidom-reference.d.ts +584 -0
  185. package/types/bundle/index.d.ts +7 -0
  186. package/types/bundle/strategies/BundleStrategy.d.ts +19 -0
  187. package/types/bundle/strategies/openapi-3-1/index.d.ts +26 -0
  188. package/types/configuration/empty.d.ts +1 -0
  189. package/types/configuration/saturated.d.ts +1 -0
  190. package/types/dereference/index.d.ts +11 -0
  191. package/types/dereference/strategies/DereferenceStrategy.d.ts +19 -0
  192. package/types/dereference/strategies/apidom/index.d.ts +30 -0
  193. package/types/dereference/strategies/apidom/selectors/element-id.d.ts +11 -0
  194. package/types/dereference/strategies/apidom/visitor.d.ts +32 -0
  195. package/types/dereference/strategies/asyncapi-2/index.d.ts +31 -0
  196. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +43 -0
  197. package/types/dereference/strategies/openapi-2/index.d.ts +32 -0
  198. package/types/dereference/strategies/openapi-2/visitor.d.ts +47 -0
  199. package/types/dereference/strategies/openapi-3-0/index.d.ts +31 -0
  200. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +49 -0
  201. package/types/dereference/strategies/openapi-3-1/index.d.ts +32 -0
  202. package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +22 -0
  203. package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +12 -0
  204. package/types/dereference/strategies/openapi-3-1/util.d.ts +21 -0
  205. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +52 -0
  206. package/types/dereference/util.d.ts +9 -0
  207. package/types/errors/BundleError.d.ts +7 -0
  208. package/types/errors/DereferenceError.d.ts +7 -0
  209. package/types/errors/EvaluationElementIdError.d.ts +7 -0
  210. package/types/errors/EvaluationJsonSchema$anchorError.d.ts +7 -0
  211. package/types/errors/EvaluationJsonSchemaUriError.d.ts +7 -0
  212. package/types/errors/InvalidJsonSchema$anchorError.d.ts +8 -0
  213. package/types/errors/JsonSchema$anchorError.d.ts +7 -0
  214. package/types/errors/JsonSchemaUriError.d.ts +7 -0
  215. package/types/errors/MaximumBundleDepthError.d.ts +7 -0
  216. package/types/errors/MaximumDereferenceDepthError.d.ts +7 -0
  217. package/types/errors/MaximumResolveDepthError.d.ts +7 -0
  218. package/types/errors/ParseError.d.ts +7 -0
  219. package/types/errors/ParserError.d.ts +7 -0
  220. package/types/errors/PluginError.d.ts +12 -0
  221. package/types/errors/ResolveError.d.ts +7 -0
  222. package/types/errors/ResolverError.d.ts +7 -0
  223. package/types/errors/UnmatchedBundleStrategyError.d.ts +7 -0
  224. package/types/errors/UnmatchedDereferenceStrategyError.d.ts +7 -0
  225. package/types/errors/UnmatchedResolveStrategyError.d.ts +7 -0
  226. package/types/errors/UnmatchedResolverError.d.ts +7 -0
  227. package/types/index.d.ts +75 -0
  228. package/types/options/index.d.ts +62 -0
  229. package/types/options/util.d.ts +5 -0
  230. package/types/parse/index.d.ts +7 -0
  231. package/types/parse/parsers/Parser.d.ts +38 -0
  232. package/types/parse/parsers/api-design-systems-json/index.d.ts +21 -0
  233. package/types/parse/parsers/api-design-systems-yaml/index.d.ts +20 -0
  234. package/types/parse/parsers/apidom-json/index.d.ts +24 -0
  235. package/types/parse/parsers/arazzo-json-1/index.d.ts +21 -0
  236. package/types/parse/parsers/arazzo-yaml-1/index.d.ts +20 -0
  237. package/types/parse/parsers/asyncapi-json-2/index.d.ts +21 -0
  238. package/types/parse/parsers/asyncapi-yaml-2/index.d.ts +20 -0
  239. package/types/parse/parsers/binary/index-browser.d.ts +21 -0
  240. package/types/parse/parsers/binary/index-node.d.ts +21 -0
  241. package/types/parse/parsers/json/index.d.ts +20 -0
  242. package/types/parse/parsers/openapi-json-2/index.d.ts +21 -0
  243. package/types/parse/parsers/openapi-json-3-0/index.d.ts +21 -0
  244. package/types/parse/parsers/openapi-json-3-1/index.d.ts +21 -0
  245. package/types/parse/parsers/openapi-yaml-2/index.d.ts +20 -0
  246. package/types/parse/parsers/openapi-yaml-3-0/index.d.ts +20 -0
  247. package/types/parse/parsers/openapi-yaml-3-1/index.d.ts +20 -0
  248. package/types/parse/parsers/yaml-1-2/index.d.ts +20 -0
  249. package/types/resolve/index.d.ts +12 -0
  250. package/types/resolve/resolvers/HTTPResolver.d.ts +22 -0
  251. package/types/resolve/resolvers/Resolver.d.ts +17 -0
  252. package/types/resolve/resolvers/file/index-browser.d.ts +12 -0
  253. package/types/resolve/resolvers/file/index-node.d.ts +20 -0
  254. package/types/resolve/resolvers/http-axios/index.d.ts +30 -0
  255. package/types/resolve/strategies/ResolveStrategy.d.ts +19 -0
  256. package/types/resolve/strategies/apidom/index.d.ts +27 -0
  257. package/types/resolve/strategies/asyncapi-2/index.d.ts +27 -0
  258. package/types/resolve/strategies/openapi-2/index.d.ts +27 -0
  259. package/types/resolve/strategies/openapi-3-0/index.d.ts +27 -0
  260. package/types/resolve/strategies/openapi-3-1/index.d.ts +27 -0
  261. package/types/resolve/util.d.ts +6 -0
  262. package/types/util/plugins.d.ts +14 -0
  263. package/types/util/url.d.ts +106 -0
@@ -0,0 +1,524 @@
1
+ import { propEq } from 'ramda';
2
+ import { RefElement, isPrimitiveElement, isStringElement, isMemberElement, isElement, IdentityManager, visit, cloneShallow, cloneDeep, toValue } from '@speclynx/apidom-core';
3
+ import { ApiDOMError } from '@speclynx/apidom-error';
4
+ import { evaluate, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
5
+ import { getNodeType, isReferenceElement, isJSONReferenceElement, isPathItemElement, isReferenceLikeElement, isJSONReferenceLikeElement, keyMap, ReferenceElement, PathItemElement } from '@speclynx/apidom-ns-openapi-2';
6
+ import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
7
+ import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
8
+ import { AncestorLineage } from "../../util.mjs";
9
+ import * as url from "../../../util/url.mjs";
10
+ import parse from "../../../parse/index.mjs";
11
+ import Reference from "../../../Reference.mjs";
12
+ // @ts-ignore
13
+ const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
14
+
15
+ // initialize element identity manager
16
+ const identityManager = new IdentityManager();
17
+
18
+ /**
19
+ * Custom mutation replacer.
20
+ * @public
21
+ */
22
+ export const mutationReplacer = (newElement, oldElement, key, parent) => {
23
+ if (isMemberElement(parent)) {
24
+ parent.value = newElement;
25
+ } else if (Array.isArray(parent)) {
26
+ parent[key] = newElement;
27
+ }
28
+ };
29
+
30
+ /**
31
+ * @public
32
+ */
33
+
34
+ /**
35
+ * @public
36
+ */
37
+ class OpenAPI2DereferenceVisitor {
38
+ indirections;
39
+ namespace;
40
+ reference;
41
+ options;
42
+ ancestors;
43
+ refractCache;
44
+ constructor({
45
+ reference,
46
+ namespace,
47
+ options,
48
+ indirections = [],
49
+ ancestors = new AncestorLineage(),
50
+ refractCache = new Map()
51
+ }) {
52
+ this.indirections = indirections;
53
+ this.namespace = namespace;
54
+ this.reference = reference;
55
+ this.options = options;
56
+ this.ancestors = new AncestorLineage(...ancestors);
57
+ this.refractCache = refractCache;
58
+ }
59
+ toBaseURI(uri) {
60
+ return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
61
+ }
62
+ async toReference(uri) {
63
+ // detect maximum depth of resolution
64
+ if (this.reference.depth >= this.options.resolve.maxDepth) {
65
+ throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);
66
+ }
67
+ const baseURI = this.toBaseURI(uri);
68
+ const {
69
+ refSet
70
+ } = this.reference;
71
+
72
+ // we've already processed this Reference in past
73
+ if (refSet.has(baseURI)) {
74
+ return refSet.find(propEq(baseURI, 'uri'));
75
+ }
76
+ const parseResult = await parse(url.unsanitize(baseURI), {
77
+ ...this.options,
78
+ parse: {
79
+ ...this.options.parse,
80
+ mediaType: 'text/plain'
81
+ }
82
+ });
83
+
84
+ // register new mutable reference with a refSet
85
+ const mutableReference = new Reference({
86
+ uri: baseURI,
87
+ value: cloneDeep(parseResult),
88
+ depth: this.reference.depth + 1
89
+ });
90
+ refSet.add(mutableReference);
91
+ if (this.options.dereference.immutable) {
92
+ // register new immutable reference with a refSet
93
+ const immutableReference = new Reference({
94
+ uri: `immutable://${baseURI}`,
95
+ value: parseResult,
96
+ depth: this.reference.depth + 1
97
+ });
98
+ refSet.add(immutableReference);
99
+ }
100
+ return mutableReference;
101
+ }
102
+ toAncestorLineage(ancestors) {
103
+ /**
104
+ * Compute full ancestors lineage.
105
+ * Ancestors are flatten to unwrap all Element instances.
106
+ */
107
+ const directAncestors = new Set(ancestors.filter(isElement));
108
+ const ancestorsLineage = new AncestorLineage(...this.ancestors, directAncestors);
109
+ return [ancestorsLineage, directAncestors];
110
+ }
111
+ async ReferenceElement(referencingElement, key, parent, path, ancestors, link) {
112
+ // skip current referencing element as it's already been access
113
+ if (this.indirections.includes(referencingElement)) {
114
+ return false;
115
+ }
116
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
117
+ const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
118
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
119
+ const isExternalReference = !isInternalReference;
120
+
121
+ // ignore resolving internal Reference Objects
122
+ if (!this.options.resolve.internal && isInternalReference) {
123
+ // skip traversing this reference element and all it's child elements
124
+ return false;
125
+ }
126
+ // ignore resolving external Reference Objects
127
+ if (!this.options.resolve.external && isExternalReference) {
128
+ // skip traversing this reference element and all it's child elements
129
+ return false;
130
+ }
131
+ const reference = await this.toReference(toValue(referencingElement.$ref));
132
+ const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
133
+ this.indirections.push(referencingElement);
134
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
135
+
136
+ // possibly non-semantic fragment
137
+ let referencedElement = evaluate(reference.value.result, jsonPointer);
138
+ referencedElement.id = identityManager.identify(referencedElement);
139
+
140
+ /**
141
+ * Applying semantics to a referenced element if semantics are missing.
142
+ */
143
+ if (isPrimitiveElement(referencedElement)) {
144
+ const referencedElementType = toValue(referencingElement.meta.get('referenced-element'));
145
+ const cacheKey = `${referencedElementType}-${toValue(identityManager.identify(referencedElement))}`;
146
+ if (this.refractCache.has(cacheKey)) {
147
+ referencedElement = this.refractCache.get(cacheKey);
148
+ } else if (isReferenceLikeElement(referencedElement)) {
149
+ // handling indirect references
150
+ referencedElement = ReferenceElement.refract(referencedElement);
151
+ referencedElement.setMetaProperty('referenced-element', referencedElementType);
152
+ this.refractCache.set(cacheKey, referencedElement);
153
+ } else {
154
+ // handling direct references
155
+ const ElementClass = this.namespace.getElementClass(referencedElementType);
156
+ referencedElement = ElementClass.refract(referencedElement);
157
+ this.refractCache.set(cacheKey, referencedElement);
158
+ }
159
+ }
160
+
161
+ // detect direct or circular reference
162
+ if (referencingElement === referencedElement) {
163
+ throw new ApiDOMError('Recursive Reference Object detected');
164
+ }
165
+
166
+ // detect maximum depth of dereferencing
167
+ if (this.indirections.length > this.options.dereference.maxDepth) {
168
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
169
+ }
170
+
171
+ // detect second deep dive into the same fragment and avoid it
172
+ if (ancestorsLineage.includes(referencedElement)) {
173
+ reference.refSet.circular = true;
174
+ if (this.options.dereference.circular === 'error') {
175
+ throw new ApiDOMError('Circular reference detected');
176
+ } else if (this.options.dereference.circular === 'replace') {
177
+ const refElement = new RefElement(referencedElement.id, {
178
+ type: 'reference',
179
+ uri: reference.uri,
180
+ $ref: toValue(referencingElement.$ref)
181
+ });
182
+ const replacer = this.options.dereference.strategyOpts['openapi-2']?.circularReplacer ?? this.options.dereference.circularReplacer;
183
+ const replacement = replacer(refElement);
184
+ link.replaceWith(replacement, mutationReplacer);
185
+ return !parent ? replacement : false;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Dive deep into the fragment.
191
+ *
192
+ * Cases to consider:
193
+ * 1. We're crossing document boundary
194
+ * 2. Fragment is from non-entry document
195
+ * 3. Fragment is a Reference Object. We need to follow it to get the eventual value
196
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
197
+ */
198
+ const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
199
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
200
+ if ((isExternalReference || isNonEntryDocument || isReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
201
+ // append referencing reference to ancestors lineage
202
+ directAncestors.add(referencingElement);
203
+ const visitor = new OpenAPI2DereferenceVisitor({
204
+ reference,
205
+ namespace: this.namespace,
206
+ indirections: [...this.indirections],
207
+ options: this.options,
208
+ refractCache: this.refractCache,
209
+ ancestors: ancestorsLineage
210
+ });
211
+ referencedElement = await visitAsync(referencedElement, visitor, {
212
+ keyMap,
213
+ nodeTypeGetter: getNodeType
214
+ });
215
+
216
+ // remove referencing reference from ancestors lineage
217
+ directAncestors.delete(referencingElement);
218
+ }
219
+ this.indirections.pop();
220
+
221
+ /**
222
+ * Creating a new version of referenced element to avoid modifying the original one.
223
+ */
224
+ const mergedElement = cloneShallow(referencedElement);
225
+ // assign unique id to merged element
226
+ mergedElement.setMetaProperty('id', identityManager.generateId());
227
+ // annotate referenced element with info about original referencing element
228
+ mergedElement.setMetaProperty('ref-fields', {
229
+ // @ts-ignore
230
+ $ref: toValue(referencingElement.$ref)
231
+ });
232
+ // annotate fragment with info about origin
233
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
234
+ // annotate fragment with info about referencing element
235
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
236
+
237
+ /**
238
+ * Transclude referencing element with merged referenced element.
239
+ */
240
+ link.replaceWith(mergedElement, mutationReplacer);
241
+
242
+ /**
243
+ * We're at the root of the tree, so we're just replacing the entire tree.
244
+ */
245
+ return !parent ? mergedElement : false;
246
+ }
247
+ async PathItemElement(referencingElement, key, parent, path, ancestors, link) {
248
+ // ignore PathItemElement without $ref field
249
+ if (!isStringElement(referencingElement.$ref)) {
250
+ return undefined;
251
+ }
252
+
253
+ // skip current referencing element as it's already been access
254
+ if (this.indirections.includes(referencingElement)) {
255
+ return false;
256
+ }
257
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
258
+ const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
259
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
260
+ const isExternalReference = !isInternalReference;
261
+
262
+ // ignore resolving internal Path Item Objects
263
+ if (!this.options.resolve.internal && isInternalReference) {
264
+ // skip traversing this Path Item element but traverse all it's child elements
265
+ return undefined;
266
+ }
267
+ // ignore resolving external Path Item Objects
268
+ if (!this.options.resolve.external && isExternalReference) {
269
+ // skip traversing this Path Item element but traverse all it's child elements
270
+ return undefined;
271
+ }
272
+ const reference = await this.toReference(toValue(referencingElement.$ref));
273
+ const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
274
+ this.indirections.push(referencingElement);
275
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
276
+
277
+ // possibly non-semantic referenced element
278
+ let referencedElement = evaluate(reference.value.result, jsonPointer);
279
+ referencedElement.id = identityManager.identify(referencedElement);
280
+
281
+ /**
282
+ * Applying semantics to a referenced element if semantics are missing.
283
+ */
284
+ if (isPrimitiveElement(referencedElement)) {
285
+ const cacheKey = `pathItem-${toValue(identityManager.identify(referencedElement))}`;
286
+ if (this.refractCache.has(cacheKey)) {
287
+ referencedElement = this.refractCache.get(cacheKey);
288
+ } else {
289
+ referencedElement = PathItemElement.refract(referencedElement);
290
+ this.refractCache.set(cacheKey, referencedElement);
291
+ }
292
+ }
293
+
294
+ // detect direct or indirect reference
295
+ if (referencingElement === referencedElement) {
296
+ throw new ApiDOMError('Recursive Path Item Object reference detected');
297
+ }
298
+
299
+ // detect maximum depth of dereferencing
300
+ if (this.indirections.length > this.options.dereference.maxDepth) {
301
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
302
+ }
303
+
304
+ // detect second deep dive into the same fragment and avoid it
305
+ if (ancestorsLineage.includes(referencedElement)) {
306
+ reference.refSet.circular = true;
307
+ if (this.options.dereference.circular === 'error') {
308
+ throw new ApiDOMError('Circular reference detected');
309
+ } else if (this.options.dereference.circular === 'replace') {
310
+ const refElement = new RefElement(referencedElement.id, {
311
+ type: 'path-item',
312
+ uri: reference.uri,
313
+ $ref: toValue(referencingElement.$ref)
314
+ });
315
+ const replacer = this.options.dereference.strategyOpts['openapi-2']?.circularReplacer ?? this.options.dereference.circularReplacer;
316
+ const replacement = replacer(refElement);
317
+ link.replaceWith(replacement, mutationReplacer);
318
+ return !parent ? replacement : false;
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Dive deep into the fragment.
324
+ *
325
+ * Cases to consider:
326
+ * 1. We're crossing document boundary
327
+ * 2. Fragment is from non-entry document
328
+ * 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
329
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
330
+ */
331
+ const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
332
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
333
+ if ((isExternalReference || isNonEntryDocument || isPathItemElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
334
+ // append referencing reference to ancestors lineage
335
+ directAncestors.add(referencingElement);
336
+ const visitor = new OpenAPI2DereferenceVisitor({
337
+ reference,
338
+ namespace: this.namespace,
339
+ indirections: [...this.indirections],
340
+ options: this.options,
341
+ refractCache: this.refractCache,
342
+ ancestors: ancestorsLineage
343
+ });
344
+ referencedElement = await visitAsync(referencedElement, visitor, {
345
+ keyMap,
346
+ nodeTypeGetter: getNodeType
347
+ });
348
+
349
+ // remove referencing reference from ancestors lineage
350
+ directAncestors.delete(referencingElement);
351
+ }
352
+ this.indirections.pop();
353
+
354
+ // merge fields from referenced Path Item with referencing one
355
+ if (isPathItemElement(referencedElement)) {
356
+ const mergedElement = new PathItemElement([...referencedElement.content], cloneDeep(referencedElement.meta), cloneDeep(referencedElement.attributes));
357
+ // assign unique id to merged element
358
+ mergedElement.setMetaProperty('id', identityManager.generateId());
359
+ // existing keywords from referencing PathItemElement overrides ones from referenced element
360
+ referencingElement.forEach((value, keyElement, item) => {
361
+ mergedElement.remove(toValue(keyElement));
362
+ mergedElement.content.push(item);
363
+ });
364
+ mergedElement.remove('$ref');
365
+
366
+ // annotate referenced element with info about original referencing element
367
+ mergedElement.setMetaProperty('ref-fields', {
368
+ $ref: toValue(referencingElement.$ref)
369
+ });
370
+ // annotate referenced element with info about origin
371
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
372
+ // annotate fragment with info about referencing element
373
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
374
+ referencedElement = mergedElement;
375
+ }
376
+
377
+ /**
378
+ * Transclude referencing element with merged referenced element.
379
+ */
380
+ link.replaceWith(referencedElement, mutationReplacer);
381
+
382
+ /**
383
+ * We're at the root of the tree, so we're just replacing the entire tree.
384
+ */
385
+ return !parent ? referencedElement : undefined;
386
+ }
387
+ async JSONReferenceElement(referencingElement, key, parent, path, ancestors, link) {
388
+ // skip current referencing element as it's already been access
389
+ if (this.indirections.includes(referencingElement)) {
390
+ return false;
391
+ }
392
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
393
+ const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
394
+ const isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
395
+ const isExternalReference = !isInternalReference;
396
+
397
+ // ignore resolving internal JSONReference Objects
398
+ if (!this.options.resolve.internal && isInternalReference) {
399
+ // skip traversing this JSONReference element and all it's child elements
400
+ return false;
401
+ }
402
+ // ignore resolving external JSONReference Objects
403
+ if (!this.options.resolve.external && isExternalReference) {
404
+ // skip traversing this JSONReference element and all it's child elements
405
+ return false;
406
+ }
407
+ const reference = await this.toReference(toValue(referencingElement.$ref));
408
+ const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
409
+ this.indirections.push(referencingElement);
410
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
411
+
412
+ // possibly non-semantic fragment
413
+ let referencedElement = evaluate(reference.value.result, jsonPointer);
414
+ referencedElement.id = identityManager.identify(referencedElement);
415
+
416
+ /**
417
+ * Applying semantics to a referenced element if semantics are missing.
418
+ */
419
+ if (isPrimitiveElement(referencedElement)) {
420
+ const referencedElementType = toValue(referencingElement.meta.get('referenced-element'));
421
+ const cacheKey = `path-item-${toValue(identityManager.identify(referencedElement))}`;
422
+ if (this.refractCache.has(cacheKey)) {
423
+ referencedElement = this.refractCache.get(cacheKey);
424
+ } else if (isJSONReferenceLikeElement(referencedElement)) {
425
+ // handling indirect references
426
+ referencedElement = ReferenceElement.refract(referencedElement);
427
+ referencedElement.setMetaProperty('referenced-element', referencedElementType);
428
+ this.refractCache.set(cacheKey, referencedElement);
429
+ } else {
430
+ // handling direct references
431
+ const ElementClass = this.namespace.getElementClass(referencedElementType);
432
+ referencedElement = ElementClass.refract(referencedElement);
433
+ this.refractCache.set(cacheKey, referencedElement);
434
+ }
435
+ }
436
+
437
+ // detect direct or circular reference
438
+ if (referencingElement === referencedElement) {
439
+ throw new ApiDOMError('Recursive Reference Object detected');
440
+ }
441
+
442
+ // detect maximum depth of dereferencing
443
+ if (this.indirections.length > this.options.dereference.maxDepth) {
444
+ throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
445
+ }
446
+
447
+ // detect second deep dive into the same fragment and avoid it
448
+ if (ancestorsLineage.includes(referencedElement)) {
449
+ reference.refSet.circular = true;
450
+ if (this.options.dereference.circular === 'error') {
451
+ throw new ApiDOMError('Circular reference detected');
452
+ } else if (this.options.dereference.circular === 'replace') {
453
+ const refElement = new RefElement(referencedElement.id, {
454
+ type: 'json-reference',
455
+ uri: reference.uri,
456
+ $ref: toValue(referencingElement.$ref)
457
+ });
458
+ const replacer = this.options.dereference.strategyOpts['openapi-2']?.circularReplacer ?? this.options.dereference.circularReplacer;
459
+ const replacement = replacer(refElement);
460
+ link.replaceWith(replacement, mutationReplacer);
461
+ return !parent ? replacement : false;
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Dive deep into the fragment.
467
+ *
468
+ * Cases to consider:
469
+ * 1. We're crossing document boundary
470
+ 2. Fragment is from non-entry document
471
+ * 3. Fragment is a JSON Reference Object. We need to follow it to get the eventual value
472
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
473
+ */
474
+ const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
475
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
476
+ if ((isExternalReference || isNonEntryDocument || isJSONReferenceElement(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
477
+ // append referencing reference to ancestors lineage
478
+ directAncestors.add(referencingElement);
479
+ const visitor = new OpenAPI2DereferenceVisitor({
480
+ reference,
481
+ namespace: this.namespace,
482
+ indirections: [...this.indirections],
483
+ options: this.options,
484
+ refractCache: this.refractCache,
485
+ ancestors: ancestorsLineage
486
+ });
487
+ referencedElement = await visitAsync(referencedElement, visitor, {
488
+ keyMap,
489
+ nodeTypeGetter: getNodeType
490
+ });
491
+
492
+ // remove referencing reference from ancestors lineage
493
+ directAncestors.delete(referencingElement);
494
+ }
495
+ this.indirections.pop();
496
+
497
+ /**
498
+ * Creating a new version of referenced element to avoid modifying the original one.
499
+ */
500
+ const mergedElement = cloneShallow(referencedElement);
501
+ // assign unique id to merged element
502
+ mergedElement.setMetaProperty('id', identityManager.generateId());
503
+ // annotate referenced element with info about original referencing element
504
+ mergedElement.setMetaProperty('ref-fields', {
505
+ // @ts-ignore
506
+ $ref: toValue(referencingElement.$ref)
507
+ });
508
+ // annotate fragment with info about origin
509
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
510
+ // annotate fragment with info about referencing element
511
+ mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
512
+
513
+ /**
514
+ * Transclude referencing element with merged referenced element.
515
+ */
516
+ link.replaceWith(mergedElement, mutationReplacer);
517
+
518
+ /**
519
+ * We're at the root of the tree, so we're just replacing the entire tree.
520
+ */
521
+ return !parent ? mergedElement : false;
522
+ }
523
+ }
524
+ export default OpenAPI2DereferenceVisitor;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
4
+ var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
5
+ exports.__esModule = true;
6
+ exports.default = void 0;
7
+ var _apidomCore = require("@speclynx/apidom-core");
8
+ var _apidomNsOpenapi = _interopRequireWildcard(require("@speclynx/apidom-ns-openapi-3-0"));
9
+ var _DereferenceStrategy = _interopRequireDefault(require("../DereferenceStrategy.cjs"));
10
+ var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
11
+ var _ReferenceSet = _interopRequireDefault(require("../../../ReferenceSet.cjs"));
12
+ var _visitor = _interopRequireDefault(require("./visitor.cjs"));
13
+ exports.OpenAPI3_0DereferenceVisitor = _visitor.default;
14
+ // @ts-ignore
15
+ const visitAsync = _apidomCore.visit[Symbol.for('nodejs.util.promisify.custom')];
16
+
17
+ /**
18
+ * @public
19
+ */
20
+
21
+ /**
22
+ * @public
23
+ */
24
+ class OpenAPI3_0DereferenceStrategy extends _DereferenceStrategy.default {
25
+ constructor(options) {
26
+ super({
27
+ ...(options ?? {}),
28
+ name: 'openapi-3-0'
29
+ });
30
+ }
31
+ canDereference(file) {
32
+ // assert by media type
33
+ if (file.mediaType !== 'text/plain') {
34
+ return _apidomNsOpenapi.mediaTypes.includes(file.mediaType);
35
+ }
36
+
37
+ // assert by inspecting ApiDOM
38
+ return (0, _apidomNsOpenapi.isOpenApi3_0Element)(file.parseResult?.api);
39
+ }
40
+ async dereference(file, options) {
41
+ const namespace = (0, _apidomCore.createNamespace)(_apidomNsOpenapi.default);
42
+ const immutableRefSet = options.dereference.refSet ?? new _ReferenceSet.default();
43
+ const mutableRefSet = new _ReferenceSet.default();
44
+ let refSet = immutableRefSet;
45
+ let reference;
46
+
47
+ // determine the initial reference
48
+ if (!immutableRefSet.has(file.uri)) {
49
+ reference = new _Reference.default({
50
+ uri: file.uri,
51
+ value: file.parseResult
52
+ });
53
+ immutableRefSet.add(reference);
54
+ } else {
55
+ // pre-computed refSet was provided as configuration option
56
+ reference = immutableRefSet.find(ref => ref.uri === file.uri);
57
+ }
58
+
59
+ /**
60
+ * Clone refSet due the dereferencing process being mutable.
61
+ * We don't want to mutate the original refSet and the references.
62
+ */
63
+ if (options.dereference.immutable) {
64
+ immutableRefSet.refs.map(ref => new _Reference.default({
65
+ ...ref,
66
+ value: (0, _apidomCore.cloneDeep)(ref.value)
67
+ })).forEach(ref => mutableRefSet.add(ref));
68
+ reference = mutableRefSet.find(ref => ref.uri === file.uri);
69
+ refSet = mutableRefSet;
70
+ }
71
+ const visitor = new _visitor.default({
72
+ reference: reference,
73
+ namespace,
74
+ options
75
+ });
76
+ const dereferencedElement = await visitAsync(refSet.rootRef.value, visitor, {
77
+ keyMap: _apidomNsOpenapi.keyMap,
78
+ nodeTypeGetter: _apidomNsOpenapi.getNodeType
79
+ });
80
+
81
+ /**
82
+ * If immutable option is set, replay refs from the refSet.
83
+ */
84
+ if (options.dereference.immutable) {
85
+ mutableRefSet.refs.filter(ref => ref.uri.startsWith('immutable://')).map(ref => new _Reference.default({
86
+ ...ref,
87
+ uri: ref.uri.replace(/^immutable:\/\//, '')
88
+ })).forEach(ref => immutableRefSet.add(ref));
89
+ }
90
+
91
+ /**
92
+ * Release all memory if this refSet was not provided as a configuration option.
93
+ * If provided as configuration option, then provider is responsible for cleanup.
94
+ */
95
+ if (options.dereference.refSet === null) {
96
+ immutableRefSet.clean();
97
+ }
98
+ mutableRefSet.clean();
99
+ return dereferencedElement;
100
+ }
101
+ }
102
+ var _default = exports.default = OpenAPI3_0DereferenceStrategy;