source: node_modules/@swagger-api/apidom-reference/es/dereference/strategies/openapi-3-1/visitor.mjs

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 25.7 KB
Line 
1import stampit from 'stampit';
2import { propEq, none } from 'ramda';
3import { isUndefined } from 'ramda-adjunct';
4import { isElement, isPrimitiveElement, isStringElement, isMemberElement, isObjectElement, IdentityManager, visit, find, cloneShallow, cloneDeep, toValue } from '@swagger-api/apidom-core';
5import { ApiDOMError } from '@swagger-api/apidom-error';
6import { evaluate as jsonPointerEvaluate, uriToPointer } from '@swagger-api/apidom-json-pointer';
7import { getNodeType, isReferenceLikeElement, keyMap, ReferenceElement, PathItemElement, OperationElement, SchemaElement, isOperationElement, isBooleanJsonSchemaElement } from '@swagger-api/apidom-ns-openapi-3-1';
8import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "./selectors/$anchor.mjs";
9import { evaluate as uriEvaluate } from "./selectors/uri.mjs";
10import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
11import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
12import * as url from "../../../util/url.mjs";
13import parse from "../../../parse/index.mjs";
14import Reference from "../../../Reference.mjs";
15import File from "../../../util/File.mjs";
16import { resolveSchema$refField, maybeRefractToSchemaElement } from "../../../resolve/strategies/openapi-3-1/util.mjs";
17import { AncestorLineage } from "../../util.mjs";
18import EvaluationJsonSchemaUriError from "../../../errors/EvaluationJsonSchemaUriError.mjs"; // @ts-ignore
19const visitAsync = visit[Symbol.for('nodejs.util.promisify.custom')];
20
21// initialize element identity manager
22const identityManager = IdentityManager();
23
24/**
25 * Predicate for detecting if element was created by merging referencing
26 * element with particular element identity with a referenced element.
27 */
28const wasReferencedBy = referencingElement => element => element.meta.hasKey('ref-referencing-element-id') && element.meta.get('ref-referencing-element-id').equals(toValue(identityManager.identify(referencingElement)));
29
30// eslint-disable-next-line @typescript-eslint/naming-convention
31const OpenApi3_1DereferenceVisitor = stampit({
32 props: {
33 indirections: null,
34 namespace: null,
35 reference: null,
36 options: null,
37 ancestors: null
38 },
39 init({
40 indirections = [],
41 reference,
42 namespace,
43 options,
44 ancestors = new AncestorLineage()
45 }) {
46 this.indirections = indirections;
47 this.namespace = namespace;
48 this.reference = reference;
49 this.options = options;
50 this.ancestors = new AncestorLineage(...ancestors);
51 },
52 methods: {
53 toBaseURI(uri) {
54 return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
55 },
56 async toReference(uri) {
57 // detect maximum depth of resolution
58 if (this.reference.depth >= this.options.resolve.maxDepth) {
59 throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);
60 }
61 const baseURI = this.toBaseURI(uri);
62 const {
63 refSet
64 } = this.reference;
65
66 // we've already processed this Reference in past
67 if (refSet.has(baseURI)) {
68 return refSet.find(propEq(baseURI, 'uri'));
69 }
70 const parseResult = await parse(url.unsanitize(baseURI), {
71 ...this.options,
72 parse: {
73 ...this.options.parse,
74 mediaType: 'text/plain'
75 }
76 });
77
78 // register new Reference with ReferenceSet
79 const reference = Reference({
80 uri: baseURI,
81 value: parseResult,
82 depth: this.reference.depth + 1
83 });
84 refSet.add(reference);
85 return reference;
86 },
87 toAncestorLineage(ancestors) {
88 /**
89 * Compute full ancestors lineage.
90 * Ancestors are flatten to unwrap all Element instances.
91 */
92 const directAncestors = new Set(ancestors.filter(isElement));
93 const ancestorsLineage = new AncestorLineage(...this.ancestors, directAncestors);
94 return [ancestorsLineage, directAncestors];
95 },
96 async ReferenceElement(referencingElement, key, parent, path, ancestors) {
97 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
98
99 // detect possible cycle in traversal and avoid it
100 if (ancestorsLineage.includesCycle(referencingElement)) {
101 return false;
102 }
103 const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
104
105 // ignore resolving external Reference Objects
106 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
107 // skip traversing this reference element and all it's child elements
108 return false;
109 }
110 const reference = await this.toReference(toValue(referencingElement.$ref));
111 const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
112 this.indirections.push(referencingElement);
113 const jsonPointer = uriToPointer($refBaseURI);
114
115 // possibly non-semantic fragment
116 let referencedElement = jsonPointerEvaluate(jsonPointer, reference.value.result);
117
118 // applying semantics to a fragment
119 if (isPrimitiveElement(referencedElement)) {
120 const referencedElementType = toValue(referencingElement.meta.get('referenced-element'));
121 if (isReferenceLikeElement(referencedElement)) {
122 // handling indirect references
123 referencedElement = ReferenceElement.refract(referencedElement);
124 referencedElement.setMetaProperty('referenced-element', referencedElementType);
125 } else {
126 // handling direct references
127 const ElementClass = this.namespace.getElementClass(referencedElementType);
128 referencedElement = ElementClass.refract(referencedElement);
129 }
130 }
131
132 // detect direct or indirect reference
133 if (this.indirections.includes(referencedElement)) {
134 throw new ApiDOMError('Recursive Reference Object detected');
135 }
136
137 // detect maximum depth of dereferencing
138 if (this.indirections.length > this.options.dereference.maxDepth) {
139 throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
140 }
141
142 // append referencing reference to ancestors lineage
143 directAncestors.add(referencingElement);
144
145 // dive deep into the fragment
146 const visitor = OpenApi3_1DereferenceVisitor({
147 reference,
148 namespace: this.namespace,
149 indirections: [...this.indirections],
150 options: this.options,
151 ancestors: ancestorsLineage
152 });
153 referencedElement = await visitAsync(referencedElement, visitor, {
154 keyMap,
155 nodeTypeGetter: getNodeType
156 });
157
158 // remove referencing reference from ancestors lineage
159 directAncestors.delete(referencingElement);
160 this.indirections.pop();
161 const mergeAndAnnotateReferencedElement = refedElement => {
162 const copy = cloneShallow(refedElement);
163
164 // annotate fragment with info about original Reference element
165 copy.setMetaProperty('ref-fields', {
166 $ref: toValue(referencingElement.$ref),
167 // @ts-ignore
168 description: toValue(referencingElement.description),
169 // @ts-ignore
170 summary: toValue(referencingElement.summary)
171 });
172 // annotate fragment with info about origin
173 copy.setMetaProperty('ref-origin', reference.uri);
174 // annotate fragment with info about referencing element
175 copy.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
176
177 // override description and summary (outer has higher priority then inner)
178 if (isObjectElement(refedElement)) {
179 if (referencingElement.hasKey('description') && 'description' in refedElement) {
180 // @ts-ignore
181 copy.remove('description');
182 // @ts-ignore
183 copy.set('description', referencingElement.get('description'));
184 }
185 if (referencingElement.hasKey('summary') && 'summary' in refedElement) {
186 // @ts-ignore
187 copy.remove('summary');
188 // @ts-ignore
189 copy.set('summary', referencingElement.get('summary'));
190 }
191 }
192 return copy;
193 };
194
195 // attempting to create cycle
196 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
197 var _ancestorsLineage$fin;
198 const replaceWith = (_ancestorsLineage$fin = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) !== null && _ancestorsLineage$fin !== void 0 ? _ancestorsLineage$fin : mergeAndAnnotateReferencedElement(referencedElement);
199 if (isMemberElement(parent)) {
200 parent.value = replaceWith; // eslint-disable-line no-param-reassign
201 } else if (Array.isArray(parent)) {
202 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
203 }
204 return false;
205 }
206
207 // transclude the element for a fragment
208 return mergeAndAnnotateReferencedElement(referencedElement);
209 },
210 async PathItemElement(referencingElement, key, parent, path, ancestors) {
211 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
212
213 // ignore PathItemElement without $ref field
214 if (!isStringElement(referencingElement.$ref)) {
215 return undefined;
216 }
217
218 // detect possible cycle in traversal and avoid it
219 if (ancestorsLineage.includesCycle(referencingElement)) {
220 return false;
221 }
222 const retrievalURI = this.toBaseURI(toValue(referencingElement.$ref));
223
224 // ignore resolving external Path Item Objects
225 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
226 // skip traversing this Path Item element but traverse all it's child elements
227 return undefined;
228 }
229 const reference = await this.toReference(toValue(referencingElement.$ref));
230 const $refBaseURI = url.resolve(retrievalURI, toValue(referencingElement.$ref));
231 this.indirections.push(referencingElement);
232 const jsonPointer = uriToPointer($refBaseURI);
233
234 // possibly non-semantic referenced element
235 let referencedElement = jsonPointerEvaluate(jsonPointer, reference.value.result);
236
237 // applying semantics to a referenced element
238 if (isPrimitiveElement(referencedElement)) {
239 referencedElement = PathItemElement.refract(referencedElement);
240 }
241
242 // detect direct or indirect reference
243 if (this.indirections.includes(referencedElement)) {
244 throw new ApiDOMError('Recursive Path Item Object reference detected');
245 }
246
247 // detect maximum depth of dereferencing
248 if (this.indirections.length > this.options.dereference.maxDepth) {
249 throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
250 }
251
252 // append referencing path item to ancestors lineage
253 directAncestors.add(referencingElement);
254
255 // dive deep into the referenced element
256 const visitor = OpenApi3_1DereferenceVisitor({
257 reference,
258 namespace: this.namespace,
259 indirections: [...this.indirections],
260 options: this.options,
261 ancestors: ancestorsLineage
262 });
263 referencedElement = await visitAsync(referencedElement, visitor, {
264 keyMap,
265 nodeTypeGetter: getNodeType
266 });
267
268 // remove referencing path item from ancestors lineage
269 directAncestors.delete(referencingElement);
270 this.indirections.pop();
271 const mergeAndAnnotateReferencedElement = refedElement => {
272 // merge fields from referenced Path Item with referencing one
273 const mergedElement = new PathItemElement([...refedElement.content], cloneDeep(refedElement.meta), cloneDeep(refedElement.attributes));
274 // existing keywords from referencing PathItemElement overrides ones from referenced element
275 referencingElement.forEach((value, keyElement, item) => {
276 mergedElement.remove(toValue(keyElement));
277 mergedElement.content.push(item);
278 });
279 mergedElement.remove('$ref');
280
281 // annotate referenced element with info about original referencing element
282 mergedElement.setMetaProperty('ref-fields', {
283 $ref: toValue(referencingElement.$ref)
284 });
285 // annotate referenced element with info about origin
286 mergedElement.setMetaProperty('ref-origin', reference.uri);
287 // annotate fragment with info about referencing element
288 mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
289 return mergedElement;
290 };
291
292 // attempting to create cycle
293 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
294 var _ancestorsLineage$fin2;
295 const replaceWith = (_ancestorsLineage$fin2 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) !== null && _ancestorsLineage$fin2 !== void 0 ? _ancestorsLineage$fin2 : mergeAndAnnotateReferencedElement(referencedElement);
296 if (isMemberElement(parent)) {
297 parent.value = replaceWith; // eslint-disable-line no-param-reassign
298 } else if (Array.isArray(parent)) {
299 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
300 }
301 return false;
302 }
303
304 // transclude referencing element with merged referenced element
305 return mergeAndAnnotateReferencedElement(referencedElement);
306 },
307 async LinkElement(linkElement) {
308 // ignore LinkElement without operationRef or operationId field
309 if (!isStringElement(linkElement.operationRef) && !isStringElement(linkElement.operationId)) {
310 return undefined;
311 }
312
313 // operationRef and operationId fields are mutually exclusive
314 if (isStringElement(linkElement.operationRef) && isStringElement(linkElement.operationId)) {
315 throw new ApiDOMError('LinkElement operationRef and operationId fields are mutually exclusive.');
316 }
317 let operationElement;
318 if (isStringElement(linkElement.operationRef)) {
319 var _linkElementCopy$oper;
320 // possibly non-semantic referenced element
321 const jsonPointer = uriToPointer(toValue(linkElement.operationRef));
322 const retrievalURI = this.toBaseURI(toValue(linkElement.operationRef));
323
324 // ignore resolving external Operation Object reference
325 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
326 // skip traversing this Link element but traverse all it's child elements
327 return undefined;
328 }
329 const reference = await this.toReference(toValue(linkElement.operationRef));
330 operationElement = jsonPointerEvaluate(jsonPointer, reference.value.result);
331 // applying semantics to a referenced element
332 if (isPrimitiveElement(operationElement)) {
333 operationElement = OperationElement.refract(operationElement);
334 }
335 // create shallow clone to be able to annotate with metadata
336 operationElement = cloneShallow(operationElement);
337 // annotate operation element with info about origin
338 operationElement.setMetaProperty('ref-origin', reference.uri);
339 const linkElementCopy = cloneShallow(linkElement);
340 (_linkElementCopy$oper = linkElementCopy.operationRef) === null || _linkElementCopy$oper === void 0 || _linkElementCopy$oper.meta.set('operation', operationElement);
341 return linkElementCopy;
342 }
343 if (isStringElement(linkElement.operationId)) {
344 var _linkElementCopy$oper2;
345 const operationId = toValue(linkElement.operationId);
346 const reference = await this.toReference(url.unsanitize(this.reference.uri));
347 operationElement = find(e => isOperationElement(e) && isElement(e.operationId) && e.operationId.equals(operationId), reference.value.result);
348 // OperationElement not found by its operationId
349 if (isUndefined(operationElement)) {
350 throw new ApiDOMError(`OperationElement(operationId=${operationId}) not found.`);
351 }
352 const linkElementCopy = cloneShallow(linkElement);
353 (_linkElementCopy$oper2 = linkElementCopy.operationId) === null || _linkElementCopy$oper2 === void 0 || _linkElementCopy$oper2.meta.set('operation', operationElement);
354 return linkElementCopy;
355 }
356 return undefined;
357 },
358 async ExampleElement(exampleElement, key, parent, path, ancestors) {
359 const [ancestorsLineage] = this.toAncestorLineage([...ancestors, parent]);
360
361 // ignore ExampleElement without externalValue field
362 if (!isStringElement(exampleElement.externalValue)) {
363 return undefined;
364 }
365
366 // detect possible cycle in traversal and avoid it
367 if (ancestorsLineage.includesCycle(exampleElement)) {
368 return false;
369 }
370
371 // value and externalValue fields are mutually exclusive
372 if (exampleElement.hasKey('value') && isStringElement(exampleElement.externalValue)) {
373 throw new ApiDOMError('ExampleElement value and externalValue fields are mutually exclusive.');
374 }
375 const retrievalURI = this.toBaseURI(toValue(exampleElement.externalValue));
376
377 // ignore resolving external Example Objects
378 if (!this.options.resolve.external && url.stripHash(this.reference.uri) !== retrievalURI) {
379 // skip traversing this Example element but traverse all it's child elements
380 return undefined;
381 }
382 const reference = await this.toReference(toValue(exampleElement.externalValue));
383
384 // shallow clone of the referenced element
385 const valueElement = cloneShallow(reference.value.result);
386 // annotate operation element with info about origin
387 valueElement.setMetaProperty('ref-origin', reference.uri);
388 const exampleElementCopy = cloneShallow(exampleElement);
389 exampleElementCopy.value = valueElement;
390 return exampleElementCopy;
391 },
392 async SchemaElement(referencingElement, key, parent, path, ancestors) {
393 const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
394
395 // skip current referencing schema as $ref keyword was not defined
396 if (!isStringElement(referencingElement.$ref)) {
397 // skip traversing this schema but traverse all it's child schemas
398 return undefined;
399 }
400
401 // detect possible cycle in traversal and avoid it
402 if (ancestorsLineage.includesCycle(referencingElement)) {
403 return false;
404 }
405
406 // compute baseURI using rules around $id and $ref keywords
407 let reference = await this.toReference(url.unsanitize(this.reference.uri));
408 let {
409 uri: retrievalURI
410 } = reference;
411 const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
412 const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
413 const file = File({
414 uri: $refBaseURIStrippedHash
415 });
416 const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
417 const isURL = !isUnknownURI;
418 const isExternalURL = uri => url.stripHash(this.reference.uri) !== uri;
419 this.indirections.push(referencingElement);
420
421 // determining reference, proper evaluation and selection mechanism
422 let referencedElement;
423 try {
424 if (isUnknownURI || isURL) {
425 // we're dealing with canonical URI or URL with possible fragment
426 const selector = $refBaseURI;
427 referencedElement = uriEvaluate(selector,
428 // @ts-ignore
429 maybeRefractToSchemaElement(reference.value.result));
430 } else {
431 // we're assuming here that we're dealing with JSON Pointer here
432 retrievalURI = this.toBaseURI(toValue($refBaseURI));
433
434 // ignore resolving external Schema Objects
435 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
436 // skip traversing this schema element but traverse all it's child elements
437 return undefined;
438 }
439 reference = await this.toReference(url.unsanitize($refBaseURI));
440 const selector = uriToPointer($refBaseURI);
441 referencedElement = maybeRefractToSchemaElement(
442 // @ts-ignore
443 jsonPointerEvaluate(selector, reference.value.result));
444 }
445 } catch (error) {
446 /**
447 * No SchemaElement($id=URL) was not found, so we're going to try to resolve
448 * the URL and assume the returned response is a JSON Schema.
449 */
450 if (isURL && error instanceof EvaluationJsonSchemaUriError) {
451 if (isAnchor(uriToAnchor($refBaseURI))) {
452 // we're dealing with JSON Schema $anchor here
453 retrievalURI = this.toBaseURI(toValue($refBaseURI));
454
455 // ignore resolving external Schema Objects
456 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
457 // skip traversing this schema element but traverse all it's child elements
458 return undefined;
459 }
460 reference = await this.toReference(url.unsanitize($refBaseURI));
461 const selector = uriToAnchor($refBaseURI);
462 referencedElement = $anchorEvaluate(selector,
463 // @ts-ignore
464 maybeRefractToSchemaElement(reference.value.result));
465 } else {
466 // we're assuming here that we're dealing with JSON Pointer here
467 retrievalURI = this.toBaseURI(toValue($refBaseURI));
468
469 // ignore resolving external Schema Objects
470 if (!this.options.resolve.external && isExternalURL(retrievalURI)) {
471 // skip traversing this schema element but traverse all it's child elements
472 return undefined;
473 }
474 reference = await this.toReference(url.unsanitize($refBaseURI));
475 const selector = uriToPointer($refBaseURI);
476 referencedElement = maybeRefractToSchemaElement(
477 // @ts-ignore
478 jsonPointerEvaluate(selector, reference.value.result));
479 }
480 } else {
481 throw error;
482 }
483 }
484
485 // detect direct or indirect reference
486 if (this.indirections.includes(referencedElement)) {
487 throw new ApiDOMError('Recursive Schema Object reference detected');
488 }
489
490 // detect maximum depth of dereferencing
491 if (this.indirections.length > this.options.dereference.maxDepth) {
492 throw new MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
493 }
494
495 // append referencing schema to ancestors lineage
496 directAncestors.add(referencingElement);
497
498 // dive deep into the fragment
499 const visitor = OpenApi3_1DereferenceVisitor({
500 reference,
501 namespace: this.namespace,
502 indirections: [...this.indirections],
503 options: this.options,
504 ancestors: ancestorsLineage
505 });
506 referencedElement = await visitAsync(referencedElement, visitor, {
507 keyMap,
508 nodeTypeGetter: getNodeType
509 });
510
511 // remove referencing schema from ancestors lineage
512 directAncestors.delete(referencingElement);
513 this.indirections.pop();
514
515 // Boolean JSON Schemas
516 if (isBooleanJsonSchemaElement(referencedElement)) {
517 const booleanJsonSchemaElement = cloneDeep(referencedElement);
518 // annotate referenced element with info about original referencing element
519 booleanJsonSchemaElement.setMetaProperty('ref-fields', {
520 $ref: toValue(referencingElement.$ref)
521 });
522 // annotate referenced element with info about origin
523 booleanJsonSchemaElement.setMetaProperty('ref-origin', reference.uri);
524 // annotate fragment with info about referencing element
525 booleanJsonSchemaElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
526 return booleanJsonSchemaElement;
527 }
528 const mergeAndAnnotateReferencedElement = refedElement => {
529 // Schema Object - merge keywords from referenced schema with referencing schema
530 const mergedElement = new SchemaElement([...refedElement.content], cloneDeep(refedElement.meta), cloneDeep(refedElement.attributes));
531 // existing keywords from referencing schema overrides ones from referenced schema
532 referencingElement.forEach((value, keyElement, item) => {
533 mergedElement.remove(toValue(keyElement));
534 mergedElement.content.push(item);
535 });
536 mergedElement.remove('$ref');
537 // annotate referenced element with info about original referencing element
538 mergedElement.setMetaProperty('ref-fields', {
539 $ref: toValue(referencingElement.$ref)
540 });
541 // annotate fragment with info about origin
542 mergedElement.setMetaProperty('ref-origin', reference.uri);
543 // annotate fragment with info about referencing element
544 mergedElement.setMetaProperty('ref-referencing-element-id', cloneDeep(identityManager.identify(referencingElement)));
545 return mergedElement;
546 };
547
548 // attempting to create cycle
549 if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
550 var _ancestorsLineage$fin3;
551 const replaceWith = (_ancestorsLineage$fin3 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) !== null && _ancestorsLineage$fin3 !== void 0 ? _ancestorsLineage$fin3 : mergeAndAnnotateReferencedElement(referencedElement);
552 if (isMemberElement(parent)) {
553 parent.value = replaceWith; // eslint-disable-line no-param-reassign
554 } else if (Array.isArray(parent)) {
555 parent[key] = replaceWith; // eslint-disable-line no-param-reassign
556 }
557 return false;
558 }
559 return mergeAndAnnotateReferencedElement(referencedElement);
560 }
561 }
562});
563export default OpenApi3_1DereferenceVisitor;
Note: See TracBrowser for help on using the repository browser.