swagger-client 3.26.8 → 3.27.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.
@@ -24,12 +24,6 @@ const visitAsync = _apidomCore.visit[Symbol.for('nodejs.util.promisify.custom')]
24
24
 
25
25
  // initialize element identity manager
26
26
  const identityManager = (0, _apidomCore.IdentityManager)();
27
-
28
- /**
29
- * Predicate for detecting if element was created by merging referencing
30
- * element with particular element identity with a referenced element.
31
- */
32
- const wasReferencedBy = referencingElement => element => element.meta.hasKey('ref-referencing-element-id') && element.meta.get('ref-referencing-element-id').equals((0, _apidomCore.toValue)(identityManager.identify(referencingElement)));
33
27
  const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1DereferenceVisitor.compose({
34
28
  props: {
35
29
  useCircularStructures: true,
@@ -48,18 +42,11 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
48
42
  methods: {
49
43
  async ReferenceElement(referencingElement, key, parent, path, ancestors) {
50
44
  try {
51
- var _this$basePath;
52
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
53
-
54
- // skip already identified cycled Path Item Objects
55
- if ((0, _apidomCore.includesClasses)(['cycle'], referencingElement.$ref)) {
56
- return false;
57
- }
58
-
59
- // detect possible cycle in traversal and avoid it
60
- if (ancestorsLineage.includesCycle(referencingElement)) {
45
+ // skip current referencing element as it's already been access
46
+ if (this.indirections.includes(referencingElement)) {
61
47
  return false;
62
48
  }
49
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
63
50
  const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(referencingElement.$ref));
64
51
  const isInternalReference = _empty.url.stripHash(this.reference.uri) === retrievalURI;
65
52
  const isExternalReference = !isInternalReference;
@@ -79,6 +66,7 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
79
66
 
80
67
  // possibly non-semantic fragment
81
68
  let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
69
+ referencedElement.id = identityManager.identify(referencedElement);
82
70
 
83
71
  // applying semantics to a fragment
84
72
  if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
@@ -100,105 +88,124 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
100
88
  }
101
89
 
102
90
  // detect direct or indirect reference
103
- if (this.indirections.includes(referencedElement)) {
104
- throw new _apidomError.ApiDOMError('Recursive JSON Pointer detected');
91
+ if (referencingElement === referencedElement) {
92
+ throw new _apidomError.ApiDOMError('Recursive Reference Object detected');
105
93
  }
106
94
 
107
95
  // detect maximum depth of dereferencing
108
96
  if (this.indirections.length > this.options.dereference.maxDepth) {
109
97
  throw new _empty.MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
110
98
  }
111
- if (!this.useCircularStructures) {
112
- const hasCycles = ancestorsLineage.includes(referencedElement);
113
- if (hasCycles) {
114
- if (_empty.url.isHttpUrl(retrievalURI) || _empty.url.isFileSystemPath(retrievalURI)) {
115
- // make the referencing URL or file system path absolute
116
- const cycledReferenceElement = new _apidomNsOpenapi.ReferenceElement({
117
- $ref: $refBaseURI
118
- }, (0, _apidomCore.cloneDeep)(referencingElement.meta), (0, _apidomCore.cloneDeep)(referencingElement.attributes));
119
- cycledReferenceElement.get('$ref').classes.push('cycle');
120
- return cycledReferenceElement;
99
+
100
+ // detect second deep dive into the same fragment and avoid it
101
+ if (ancestorsLineage.includes(referencedElement)) {
102
+ reference.refSet.circular = true;
103
+ if (this.options.dereference.circular === 'error') {
104
+ throw new _apidomError.ApiDOMError('Circular reference detected');
105
+ } else if (this.options.dereference.circular === 'replace') {
106
+ var _this$options$derefer, _this$options$derefer2;
107
+ const refElement = new _apidomCore.RefElement(referencedElement.id, {
108
+ type: 'reference',
109
+ uri: reference.uri,
110
+ $ref: (0, _apidomCore.toValue)(referencingElement.$ref),
111
+ baseURI: $refBaseURI,
112
+ referencingElement
113
+ });
114
+ const replacer = (_this$options$derefer = (_this$options$derefer2 = this.options.dereference.strategyOpts['openapi-3-1']) == null ? void 0 : _this$options$derefer2.circularReplacer) != null ? _this$options$derefer : this.options.dereference.circularReplacer;
115
+ const replacement = replacer(refElement);
116
+ if ((0, _apidomCore.isMemberElement)(parent)) {
117
+ parent.value = replacement; // eslint-disable-line no-param-reassign
118
+ } else if (Array.isArray(parent)) {
119
+ parent[key] = replacement; // eslint-disable-line no-param-reassign
121
120
  }
122
- // skip processing this reference
123
- return false;
121
+ return !parent ? replacement : false;
124
122
  }
125
123
  }
126
124
 
127
- // append referencing schema to ancestors lineage
128
- directAncestors.add(referencingElement);
129
-
130
- // dive deep into the fragment
131
- const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({
132
- reference,
133
- namespace: this.namespace,
134
- indirections: [...this.indirections],
135
- options: this.options,
136
- ancestors: ancestorsLineage,
137
- allowMetaPatches: this.allowMetaPatches,
138
- useCircularStructures: this.useCircularStructures,
139
- basePath: (_this$basePath = this.basePath) != null ? _this$basePath : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
140
- });
141
- referencedElement = await visitAsync(referencedElement, visitor, {
142
- keyMap: _apidomNsOpenapi.keyMap,
143
- nodeTypeGetter: _apidomNsOpenapi.getNodeType
144
- });
125
+ /**
126
+ * Dive deep into the fragment.
127
+ *
128
+ * Cases to consider:
129
+ * 1. We're crossing document boundary
130
+ * 2. Fragment is from non-root document
131
+ * 3. Fragment is a Reference Object. We need to follow it to get the eventual value
132
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
133
+ */
134
+ const isNonRootDocument = _empty.url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
135
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
136
+ if ((isExternalReference || isNonRootDocument || (0, _apidomNsOpenapi.isReferenceElement)(referencedElement) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
137
+ var _this$basePath;
138
+ // append referencing reference to ancestors lineage
139
+ directAncestors.add(referencingElement);
140
+ const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({
141
+ reference,
142
+ namespace: this.namespace,
143
+ indirections: [...this.indirections],
144
+ options: this.options,
145
+ refractCache: this.refractCache,
146
+ ancestors: ancestorsLineage,
147
+ allowMetaPatches: this.allowMetaPatches,
148
+ useCircularStructures: this.useCircularStructures,
149
+ basePath: (_this$basePath = this.basePath) != null ? _this$basePath : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
150
+ });
151
+ referencedElement = await visitAsync(referencedElement, visitor, {
152
+ keyMap: _apidomNsOpenapi.keyMap,
153
+ nodeTypeGetter: _apidomNsOpenapi.getNodeType
154
+ });
145
155
 
146
- // remove referencing schema from ancestors lineage
147
- directAncestors.delete(referencingElement);
156
+ // remove referencing reference from ancestors lineage
157
+ directAncestors.delete(referencingElement);
158
+ }
148
159
  this.indirections.pop();
149
- const mergeAndAnnotateReferencedElement = refedElement => {
150
- const copy = (0, _apidomCore.cloneShallow)(refedElement);
151
-
152
- // annotate fragment with info about original Reference element
153
- copy.setMetaProperty('ref-fields', {
154
- $ref: (0, _apidomCore.toValue)(referencingElement.$ref),
155
- description: (0, _apidomCore.toValue)(referencingElement.description),
156
- summary: (0, _apidomCore.toValue)(referencingElement.summary)
157
- });
158
- // annotate fragment with info about origin
159
- copy.setMetaProperty('ref-origin', reference.uri);
160
- // annotate fragment with info about referencing element
161
- copy.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
160
+ const mergedElement = (0, _apidomCore.cloneShallow)(referencedElement);
162
161
 
163
- // override description and summary (outer has higher priority then inner)
164
- if ((0, _apidomCore.isObjectElement)(refedElement)) {
165
- if (referencingElement.hasKey('description') && 'description' in refedElement) {
166
- copy.remove('description');
167
- copy.set('description', referencingElement.get('description'));
168
- }
169
- if (referencingElement.hasKey('summary') && 'summary' in refedElement) {
170
- copy.remove('summary');
171
- copy.set('summary', referencingElement.get('summary'));
172
- }
162
+ // annotate fragment with info about original Reference element
163
+ mergedElement.setMetaProperty('ref-fields', {
164
+ $ref: (0, _apidomCore.toValue)(referencingElement.$ref),
165
+ description: (0, _apidomCore.toValue)(referencingElement.description),
166
+ summary: (0, _apidomCore.toValue)(referencingElement.summary)
167
+ });
168
+ // annotate fragment with info about origin
169
+ mergedElement.setMetaProperty('ref-origin', reference.uri);
170
+ // annotate fragment with info about referencing element
171
+ mergedElement.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
172
+
173
+ // override description and summary (outer has higher priority then inner)
174
+ if ((0, _apidomCore.isObjectElement)(referencedElement)) {
175
+ if (referencingElement.hasKey('description') && 'description' in referencedElement) {
176
+ mergedElement.remove('description');
177
+ mergedElement.set('description', referencingElement.get('description'));
173
178
  }
174
-
175
- // apply meta patches
176
- if (this.allowMetaPatches && (0, _apidomCore.isObjectElement)(copy)) {
177
- // apply meta patch only when not already applied
178
- if (!copy.hasKey('$$ref')) {
179
- const baseURI = _empty.url.resolve(retrievalURI, $refBaseURI);
180
- copy.set('$$ref', baseURI);
181
- }
179
+ if (referencingElement.hasKey('summary') && 'summary' in referencedElement) {
180
+ mergedElement.remove('summary');
181
+ mergedElement.set('summary', referencingElement.get('summary'));
182
182
  }
183
- return copy;
184
- };
183
+ }
185
184
 
186
- // attempting to create cycle
187
- if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
188
- var _ancestorsLineage$fin;
189
- const replaceWith = (_ancestorsLineage$fin = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin : mergeAndAnnotateReferencedElement(referencedElement);
190
- if ((0, _apidomCore.isMemberElement)(parent)) {
191
- parent.value = replaceWith; // eslint-disable-line no-param-reassign
192
- } else if (Array.isArray(parent)) {
193
- parent[key] = replaceWith; // eslint-disable-line no-param-reassign
185
+ // apply meta patches
186
+ if (this.allowMetaPatches && (0, _apidomCore.isObjectElement)(mergedElement)) {
187
+ // apply meta patch only when not already applied
188
+ if (!mergedElement.hasKey('$$ref')) {
189
+ const baseURI = _empty.url.resolve(retrievalURI, $refBaseURI);
190
+ mergedElement.set('$$ref', baseURI);
194
191
  }
195
- return false;
196
192
  }
197
193
 
198
- // transclude the element for a fragment
199
- return mergeAndAnnotateReferencedElement(referencedElement);
194
+ /**
195
+ * Transclude referencing element with merged referenced element.
196
+ */
197
+ if ((0, _apidomCore.isMemberElement)(parent)) {
198
+ parent.value = mergedElement; // eslint-disable-line no-param-reassign
199
+ } else if (Array.isArray(parent)) {
200
+ parent[key] = mergedElement; // eslint-disable-line no-param-reassign
201
+ }
202
+
203
+ /**
204
+ * We're at the root of the tree, so we're just replacing the entire tree.
205
+ */
206
+ return !parent ? mergedElement : false;
200
207
  } catch (error) {
201
- var _this$basePath2, _this$options$derefer;
208
+ var _this$basePath2, _this$options$derefer3;
202
209
  const rootCause = (0, _getRootCause.default)(error);
203
210
  const wrappedError = wrapError(rootCause, {
204
211
  baseDoc: this.reference.uri,
@@ -206,29 +213,27 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
206
213
  pointer: (0, _apidomJsonPointer.uriToPointer)((0, _apidomCore.toValue)(referencingElement.$ref)),
207
214
  fullPath: (_this$basePath2 = this.basePath) != null ? _this$basePath2 : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
208
215
  });
209
- (_this$options$derefer = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer = _this$options$derefer.errors) == null || _this$options$derefer.push == null || _this$options$derefer.push(wrappedError);
216
+ (_this$options$derefer3 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer3 = _this$options$derefer3.errors) == null || _this$options$derefer3.push == null || _this$options$derefer3.push(wrappedError);
210
217
  return undefined;
211
218
  }
212
219
  },
213
220
  async PathItemElement(pathItemElement, key, parent, path, ancestors) {
214
221
  try {
215
- var _this$basePath3;
216
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
217
-
218
222
  // ignore PathItemElement without $ref field
219
223
  if (!(0, _apidomCore.isStringElement)(pathItemElement.$ref)) {
220
224
  return undefined;
221
225
  }
222
226
 
223
- // skip already identified cycled Path Item Objects
224
- if ((0, _apidomCore.includesClasses)(['cycle'], pathItemElement.$ref)) {
227
+ // skip current referencing element as it's already been access
228
+ if (this.indirections.includes(pathItemElement)) {
225
229
  return false;
226
230
  }
227
231
 
228
- // detect possible cycle in traversal and avoid it
229
- if (ancestorsLineage.includesCycle(pathItemElement)) {
232
+ // skip already identified cycled Path Item Objects
233
+ if ((0, _apidomCore.includesClasses)(['cycle'], pathItemElement.$ref)) {
230
234
  return false;
231
235
  }
236
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
232
237
  const retrievalURI = this.toBaseURI((0, _apidomCore.toValue)(pathItemElement.$ref));
233
238
  const isInternalReference = _empty.url.stripHash(this.reference.uri) === retrievalURI;
234
239
  const isExternalReference = !isInternalReference;
@@ -248,10 +253,11 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
248
253
 
249
254
  // possibly non-semantic referenced element
250
255
  let referencedElement = (0, _apidomJsonPointer.evaluate)(jsonPointer, reference.value.result);
256
+ referencedElement.id = identityManager.identify(referencedElement);
251
257
 
252
258
  // applying semantics to a referenced element
253
259
  if ((0, _apidomCore.isPrimitiveElement)(referencedElement)) {
254
- const cacheKey = `pathItem-${(0, _apidomCore.toValue)(identityManager.identify(referencedElement))}`;
260
+ const cacheKey = `path-item-${(0, _apidomCore.toValue)(identityManager.identify(referencedElement))}`;
255
261
  if (this.refractCache.has(cacheKey)) {
256
262
  referencedElement = this.refractCache.get(cacheKey);
257
263
  } else {
@@ -261,55 +267,82 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
261
267
  }
262
268
 
263
269
  // detect direct or indirect reference
264
- if (this.indirections.includes(referencedElement)) {
265
- throw new _apidomError.ApiDOMError('Recursive JSON Pointer detected');
270
+ if (pathItemElement === referencedElement) {
271
+ throw new _apidomError.ApiDOMError('Recursive Path Item Object reference detected');
266
272
  }
267
273
 
268
274
  // detect maximum depth of dereferencing
269
275
  if (this.indirections.length > this.options.dereference.maxDepth) {
270
276
  throw new _empty.MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
271
277
  }
272
- if (!this.useCircularStructures) {
273
- const hasCycles = ancestorsLineage.includes(referencedElement);
274
- if (hasCycles) {
275
- if (_empty.url.isHttpUrl(retrievalURI) || _empty.url.isFileSystemPath(retrievalURI)) {
276
- // make the referencing URL or file system path absolute
277
- const cycledPathItemElement = new _apidomNsOpenapi.PathItemElement({
278
- $ref: $refBaseURI
279
- }, (0, _apidomCore.cloneDeep)(pathItemElement.meta), (0, _apidomCore.cloneDeep)(pathItemElement.attributes));
280
- cycledPathItemElement.get('$ref').classes.push('cycle');
281
- return cycledPathItemElement;
278
+
279
+ // detect second deep dive into the same fragment and avoid it
280
+ if (ancestorsLineage.includes(referencedElement)) {
281
+ reference.refSet.circular = true;
282
+ if (this.options.dereference.circular === 'error') {
283
+ throw new _apidomError.ApiDOMError('Circular reference detected');
284
+ } else if (this.options.dereference.circular === 'replace') {
285
+ var _this$options$derefer4, _this$options$derefer5;
286
+ const refElement = new _apidomCore.RefElement(referencedElement.id, {
287
+ type: 'path-item',
288
+ uri: reference.uri,
289
+ $ref: (0, _apidomCore.toValue)(pathItemElement.$ref),
290
+ baseURI: $refBaseURI,
291
+ referencingElement: pathItemElement
292
+ });
293
+ const replacer = (_this$options$derefer4 = (_this$options$derefer5 = this.options.dereference.strategyOpts['openapi-3-1']) == null ? void 0 : _this$options$derefer5.circularReplacer) != null ? _this$options$derefer4 : this.options.dereference.circularReplacer;
294
+ const replacement = replacer(refElement);
295
+ if ((0, _apidomCore.isMemberElement)(parent)) {
296
+ parent.value = replacement; // eslint-disable-line no-param-reassign
297
+ } else if (Array.isArray(parent)) {
298
+ parent[key] = replacement; // eslint-disable-line no-param-reassign
282
299
  }
283
- // skip processing this path item and all it's child elements
284
- return false;
300
+ return !parent ? replacement : false;
285
301
  }
286
302
  }
287
303
 
288
- // append referencing schema to ancestors lineage
289
- directAncestors.add(pathItemElement);
290
-
291
- // dive deep into the referenced element
292
- const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({
293
- reference,
294
- namespace: this.namespace,
295
- indirections: [...this.indirections],
296
- options: this.options,
297
- ancestors: ancestorsLineage,
298
- allowMetaPatches: this.allowMetaPatches,
299
- useCircularStructures: this.useCircularStructures,
300
- basePath: (_this$basePath3 = this.basePath) != null ? _this$basePath3 : [...(0, _toPath.default)([...ancestors, parent, pathItemElement]), '$ref']
301
- });
302
- referencedElement = await visitAsync(referencedElement, visitor, {
303
- keyMap: _apidomNsOpenapi.keyMap,
304
- nodeTypeGetter: _apidomNsOpenapi.getNodeType
305
- });
304
+ /**
305
+ * Dive deep into the fragment.
306
+ *
307
+ * Cases to consider:
308
+ * 1. We're crossing document boundary
309
+ * 2. Fragment is from non-root document
310
+ * 3. Fragment is a Path Item Object with $ref field. We need to follow it to get the eventual value
311
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
312
+ */
313
+ const isNonRootDocument = _empty.url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
314
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
315
+ if ((isExternalReference || isNonRootDocument || (0, _apidomNsOpenapi.isPathItemElement)(referencedElement) && (0, _apidomCore.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
316
+ var _this$basePath3;
317
+ // append referencing schema to ancestors lineage
318
+ directAncestors.add(pathItemElement);
319
+
320
+ // dive deep into the referenced element
321
+ const visitor = OpenApi3_1SwaggerClientDereferenceVisitor({
322
+ reference,
323
+ namespace: this.namespace,
324
+ indirections: [...this.indirections],
325
+ options: this.options,
326
+ ancestors: ancestorsLineage,
327
+ allowMetaPatches: this.allowMetaPatches,
328
+ useCircularStructures: this.useCircularStructures,
329
+ basePath: (_this$basePath3 = this.basePath) != null ? _this$basePath3 : [...(0, _toPath.default)([...ancestors, parent, pathItemElement]), '$ref']
330
+ });
331
+ referencedElement = await visitAsync(referencedElement, visitor, {
332
+ keyMap: _apidomNsOpenapi.keyMap,
333
+ nodeTypeGetter: _apidomNsOpenapi.getNodeType
334
+ });
306
335
 
307
- // remove referencing schema from ancestors lineage
308
- directAncestors.delete(pathItemElement);
336
+ // remove referencing schema from ancestors lineage
337
+ directAncestors.delete(pathItemElement);
338
+ }
309
339
  this.indirections.pop();
310
- const mergeAndAnnotateReferencedElement = refedElement => {
311
- // merge fields from referenced Path Item with referencing one
312
- const mergedElement = new _apidomNsOpenapi.PathItemElement([...refedElement.content], (0, _apidomCore.cloneDeep)(refedElement.meta), (0, _apidomCore.cloneDeep)(refedElement.attributes));
340
+
341
+ /**
342
+ * Creating a new version of Path Item by merging fields from referenced Path Item with referencing one.
343
+ */
344
+ if ((0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
345
+ const mergedElement = new _apidomNsOpenapi.PathItemElement([...referencedElement.content], (0, _apidomCore.cloneDeep)(referencedElement.meta), (0, _apidomCore.cloneDeep)(referencedElement.attributes));
313
346
  // existing keywords from referencing PathItemElement overrides ones from referenced element
314
347
  pathItemElement.forEach((value, keyElement, item) => {
315
348
  mergedElement.remove((0, _apidomCore.toValue)(keyElement));
@@ -334,25 +367,24 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
334
367
  mergedElement.set('$$ref', baseURI);
335
368
  }
336
369
  }
337
- return mergedElement;
338
- };
370
+ referencedElement = mergedElement;
371
+ }
339
372
 
340
- // attempting to create cycle
341
- if (ancestorsLineage.includes(pathItemElement) || ancestorsLineage.includes(referencedElement)) {
342
- var _ancestorsLineage$fin2;
343
- const replaceWith = (_ancestorsLineage$fin2 = ancestorsLineage.findItem(wasReferencedBy(pathItemElement))) != null ? _ancestorsLineage$fin2 : mergeAndAnnotateReferencedElement(referencedElement);
344
- if ((0, _apidomCore.isMemberElement)(parent)) {
345
- parent.value = replaceWith; // eslint-disable-line no-param-reassign
346
- } else if (Array.isArray(parent)) {
347
- parent[key] = replaceWith; // eslint-disable-line no-param-reassign
348
- }
349
- return false;
373
+ /**
374
+ * Transclude referencing element with merged referenced element.
375
+ */
376
+ if ((0, _apidomCore.isMemberElement)(parent)) {
377
+ parent.value = referencedElement; // eslint-disable-line no-param-reassign
378
+ } else if (Array.isArray(parent)) {
379
+ parent[key] = referencedElement; // eslint-disable-line no-param-reassign
350
380
  }
351
381
 
352
- // transclude referencing element with merged referenced element
353
- return mergeAndAnnotateReferencedElement(referencedElement);
382
+ /**
383
+ * We're at the root of the tree, so we're just replacing the entire tree.
384
+ */
385
+ return !parent ? referencedElement : undefined;
354
386
  } catch (error) {
355
- var _this$basePath4, _this$options$derefer2;
387
+ var _this$basePath4, _this$options$derefer6;
356
388
  const rootCause = (0, _getRootCause.default)(error);
357
389
  const wrappedError = wrapError(rootCause, {
358
390
  baseDoc: this.reference.uri,
@@ -360,30 +392,23 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
360
392
  pointer: (0, _apidomJsonPointer.uriToPointer)((0, _apidomCore.toValue)(pathItemElement.$ref)),
361
393
  fullPath: (_this$basePath4 = this.basePath) != null ? _this$basePath4 : [...(0, _toPath.default)([...ancestors, parent, pathItemElement]), '$ref']
362
394
  });
363
- (_this$options$derefer2 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer2 = _this$options$derefer2.errors) == null || _this$options$derefer2.push == null || _this$options$derefer2.push(wrappedError);
395
+ (_this$options$derefer6 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer6 = _this$options$derefer6.errors) == null || _this$options$derefer6.push == null || _this$options$derefer6.push(wrappedError);
364
396
  return undefined;
365
397
  }
366
398
  },
367
399
  async SchemaElement(referencingElement, key, parent, path, ancestors) {
368
400
  try {
369
- var _this$basePath5;
370
- const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
371
-
372
401
  // skip current referencing schema as $ref keyword was not defined
373
402
  if (!(0, _apidomCore.isStringElement)(referencingElement.$ref)) {
374
403
  // skip traversing this schema but traverse all it's child schemas
375
404
  return undefined;
376
405
  }
377
406
 
378
- // skip already identified cycled Path Item Objects
379
- if ((0, _apidomCore.includesClasses)(['cycle'], referencingElement.$ref)) {
380
- return false;
381
- }
382
-
383
- // detect possible cycle in traversal and avoid it
384
- if (ancestorsLineage.includesCycle(referencingElement)) {
407
+ // skip current referencing element as it's already been access
408
+ if (this.indirections.includes(referencingElement)) {
385
409
  return false;
386
410
  }
411
+ const [ancestorsLineage, directAncestors] = this.toAncestorLineage([...ancestors, parent]);
387
412
 
388
413
  // compute baseURI using rules around $id and $ref keywords
389
414
  let reference = await this.toReference(_empty.url.unsanitize(this.reference.uri));
@@ -397,8 +422,8 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
397
422
  });
398
423
  const isUnknownURI = !this.options.resolve.resolvers.some(r => r.canRead(file));
399
424
  const isURL = !isUnknownURI;
400
- const isInternalReference = uri => _empty.url.stripHash(this.reference.uri) === uri;
401
- const isExternalReference = uri => !isInternalReference(uri);
425
+ let isInternalReference = _empty.url.stripHash(this.reference.uri) === $refBaseURI;
426
+ let isExternalReference = !isInternalReference;
402
427
  this.indirections.push(referencingElement);
403
428
 
404
429
  // determining reference, proper evaluation and selection mechanism
@@ -406,25 +431,45 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
406
431
  try {
407
432
  if (isUnknownURI || isURL) {
408
433
  // we're dealing with canonical URI or URL with possible fragment
434
+ retrievalURI = this.toBaseURI($refBaseURI);
409
435
  const selector = $refBaseURI;
410
- referencedElement = (0, _uri.evaluate)(selector, (0, _openapi.maybeRefractToSchemaElement)(reference.value.result));
436
+ const referenceAsSchema = (0, _openapi.maybeRefractToSchemaElement)(reference.value.result);
437
+ referencedElement = (0, _uri.evaluate)(selector, referenceAsSchema);
438
+ referencedElement = (0, _openapi.maybeRefractToSchemaElement)(referencedElement);
439
+ referencedElement.id = identityManager.identify(referencedElement);
440
+
441
+ // ignore resolving internal Schema Objects
442
+ if (!this.options.resolve.internal && isInternalReference) {
443
+ // skip traversing this schema element but traverse all it's child elements
444
+ return undefined;
445
+ }
446
+ // ignore resolving external Schema Objects
447
+ if (!this.options.resolve.external && isExternalReference) {
448
+ // skip traversing this schema element but traverse all it's child elements
449
+ return undefined;
450
+ }
411
451
  } else {
412
452
  // we're assuming here that we're dealing with JSON Pointer here
413
- retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
453
+ retrievalURI = this.toBaseURI($refBaseURI);
454
+ isInternalReference = _empty.url.stripHash(this.reference.uri) === retrievalURI;
455
+ isExternalReference = !isInternalReference;
414
456
 
415
457
  // ignore resolving internal Schema Objects
416
- if (!this.options.resolve.internal && isInternalReference(retrievalURI)) {
458
+ if (!this.options.resolve.internal && isInternalReference) {
417
459
  // skip traversing this schema element but traverse all it's child elements
418
460
  return undefined;
419
461
  }
420
462
  // ignore resolving external Schema Objects
421
- if (!this.options.resolve.external && isExternalReference(retrievalURI)) {
463
+ if (!this.options.resolve.external && isExternalReference) {
422
464
  // skip traversing this schema element but traverse all it's child elements
423
465
  return undefined;
424
466
  }
425
467
  reference = await this.toReference(_empty.url.unsanitize($refBaseURI));
426
468
  const selector = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
427
- referencedElement = (0, _openapi.maybeRefractToSchemaElement)((0, _apidomJsonPointer.evaluate)(selector, reference.value.result));
469
+ const referenceAsSchema = (0, _openapi.maybeRefractToSchemaElement)(reference.value.result);
470
+ referencedElement = (0, _apidomJsonPointer.evaluate)(selector, referenceAsSchema);
471
+ referencedElement = (0, _openapi.maybeRefractToSchemaElement)(referencedElement);
472
+ referencedElement.id = identityManager.identify(referencedElement);
428
473
  }
429
474
  } catch (error) {
430
475
  /**
@@ -434,38 +479,47 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
434
479
  if (isURL && error instanceof _uri.EvaluationJsonSchemaUriError) {
435
480
  if ((0, _$anchor.isAnchor)((0, _$anchor.uriToAnchor)($refBaseURI))) {
436
481
  // we're dealing with JSON Schema $anchor here
437
- retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
482
+ isInternalReference = _empty.url.stripHash(this.reference.uri) === retrievalURI;
483
+ isExternalReference = !isInternalReference;
438
484
 
439
485
  // ignore resolving internal Schema Objects
440
- if (!this.options.resolve.internal && isInternalReference(retrievalURI)) {
486
+ if (!this.options.resolve.internal && isInternalReference) {
441
487
  // skip traversing this schema element but traverse all it's child elements
442
488
  return undefined;
443
489
  }
444
490
  // ignore resolving external Schema Objects
445
- if (!this.options.resolve.external && isExternalReference(retrievalURI)) {
491
+ if (!this.options.resolve.external && isExternalReference) {
446
492
  // skip traversing this schema element but traverse all it's child elements
447
493
  return undefined;
448
494
  }
449
495
  reference = await this.toReference(_empty.url.unsanitize($refBaseURI));
450
496
  const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
451
- referencedElement = (0, _$anchor.evaluate)(selector, (0, _openapi.maybeRefractToSchemaElement)(reference.value.result));
497
+ const referenceAsSchema = (0, _openapi.maybeRefractToSchemaElement)(reference.value.result);
498
+ referencedElement = (0, _$anchor.evaluate)(selector, referenceAsSchema);
499
+ referencedElement = (0, _openapi.maybeRefractToSchemaElement)(referencedElement);
500
+ referencedElement.id = identityManager.identify(referencedElement);
452
501
  } else {
453
502
  // we're assuming here that we're dealing with JSON Pointer here
454
503
  retrievalURI = this.toBaseURI((0, _apidomCore.toValue)($refBaseURI));
504
+ isInternalReference = _empty.url.stripHash(this.reference.uri) === retrievalURI;
505
+ isExternalReference = !isInternalReference;
455
506
 
456
507
  // ignore resolving internal Schema Objects
457
- if (!this.options.resolve.internal && isInternalReference(retrievalURI)) {
508
+ if (!this.options.resolve.internal && isInternalReference) {
458
509
  // skip traversing this schema element but traverse all it's child elements
459
510
  return undefined;
460
511
  }
461
512
  // ignore resolving external Schema Objects
462
- if (!this.options.resolve.external && isExternalReference(retrievalURI)) {
513
+ if (!this.options.resolve.external && isExternalReference) {
463
514
  // skip traversing this schema element but traverse all it's child elements
464
515
  return undefined;
465
516
  }
466
517
  reference = await this.toReference(_empty.url.unsanitize($refBaseURI));
467
518
  const selector = (0, _apidomJsonPointer.uriToPointer)($refBaseURI);
468
- referencedElement = (0, _openapi.maybeRefractToSchemaElement)((0, _apidomJsonPointer.evaluate)(selector, reference.value.result));
519
+ const referenceAsSchema = (0, _openapi.maybeRefractToSchemaElement)(reference.value.result);
520
+ referencedElement = (0, _apidomJsonPointer.evaluate)(selector, referenceAsSchema);
521
+ referencedElement = (0, _openapi.maybeRefractToSchemaElement)(referencedElement);
522
+ referencedElement.id = identityManager.identify(referencedElement);
469
523
  }
470
524
  } else {
471
525
  throw error;
@@ -473,7 +527,7 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
473
527
  }
474
528
 
475
529
  // detect direct or indirect reference
476
- if (this.indirections.includes(referencedElement)) {
530
+ if (referencingElement === referencedElement) {
477
531
  throw new _apidomError.ApiDOMError('Recursive Schema Object reference detected');
478
532
  }
479
533
 
@@ -482,45 +536,66 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
482
536
  throw new _empty.MaximumDereferenceDepthError(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);
483
537
  }
484
538
 
485
- // useCircularStructures option processing
486
- if (!this.useCircularStructures) {
487
- const hasCycles = ancestorsLineage.includes(referencedElement);
488
- if (hasCycles) {
489
- if (_empty.url.isHttpUrl(retrievalURI) || _empty.url.isFileSystemPath(retrievalURI)) {
490
- // make the referencing URL or file system path absolute
491
- const baseURI = _empty.url.resolve(retrievalURI, $refBaseURI);
492
- const cycledSchemaElement = new _apidomNsOpenapi.SchemaElement({
493
- $ref: baseURI
494
- }, (0, _apidomCore.cloneDeep)(referencingElement.meta), (0, _apidomCore.cloneDeep)(referencingElement.attributes));
495
- cycledSchemaElement.get('$ref').classes.push('cycle');
496
- return cycledSchemaElement;
539
+ // detect second deep dive into the same fragment and avoid it
540
+ if (ancestorsLineage.includes(referencedElement)) {
541
+ reference.refSet.circular = true;
542
+ if (this.options.dereference.circular === 'error') {
543
+ throw new _apidomError.ApiDOMError('Circular reference detected');
544
+ } else if (this.options.dereference.circular === 'replace') {
545
+ var _this$options$derefer7, _this$options$derefer8;
546
+ const refElement = new _apidomCore.RefElement(referencedElement.id, {
547
+ type: 'json-schema',
548
+ uri: reference.uri,
549
+ $ref: (0, _apidomCore.toValue)(referencingElement.$ref),
550
+ baseURI: _empty.url.resolve(retrievalURI, $refBaseURI),
551
+ referencingElement
552
+ });
553
+ const replacer = (_this$options$derefer7 = (_this$options$derefer8 = this.options.dereference.strategyOpts['openapi-3-1']) == null ? void 0 : _this$options$derefer8.circularReplacer) != null ? _this$options$derefer7 : this.options.dereference.circularReplacer;
554
+ const replacement = replacer(refElement);
555
+ if ((0, _apidomCore.isMemberElement)(parent)) {
556
+ parent.value = replacement; // eslint-disable-line no-param-reassign
557
+ } else if (Array.isArray(parent)) {
558
+ parent[key] = replacement; // eslint-disable-line no-param-reassign
497
559
  }
498
- // skip processing this schema and all it's child schemas
499
- return false;
560
+ return !parent ? replacement : false;
500
561
  }
501
562
  }
502
563
 
503
- // append referencing schema to ancestors lineage
504
- directAncestors.add(referencingElement);
505
-
506
- // dive deep into the fragment
507
- const mergeVisitor = OpenApi3_1SwaggerClientDereferenceVisitor({
508
- reference,
509
- namespace: this.namespace,
510
- indirections: [...this.indirections],
511
- options: this.options,
512
- useCircularStructures: this.useCircularStructures,
513
- allowMetaPatches: this.allowMetaPatches,
514
- ancestors: ancestorsLineage,
515
- basePath: (_this$basePath5 = this.basePath) != null ? _this$basePath5 : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
516
- });
517
- referencedElement = await visitAsync(referencedElement, mergeVisitor, {
518
- keyMap: _apidomNsOpenapi.keyMap,
519
- nodeTypeGetter: _apidomNsOpenapi.getNodeType
520
- });
564
+ /**
565
+ * Dive deep into the fragment.
566
+ *
567
+ * Cases to consider:
568
+ * 1. We're crossing document boundary
569
+ * 2. Fragment is from non-root document
570
+ * 3. Fragment is a Schema Object with $ref field. We need to follow it to get the eventual value
571
+ * 4. We are dereferencing the fragment lazily/eagerly depending on circular mode
572
+ */
573
+ const isNonRootDocument = _empty.url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
574
+ const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
575
+ if ((isExternalReference || isNonRootDocument || (0, _apidomNsOpenapi.isSchemaElement)(referencedElement) && (0, _apidomCore.isStringElement)(referencedElement.$ref) || shouldDetectCircular) && !ancestorsLineage.includesCycle(referencedElement)) {
576
+ var _this$basePath5;
577
+ // append referencing schema to ancestors lineage
578
+ directAncestors.add(referencingElement);
579
+
580
+ // dive deep into the fragment
581
+ const mergeVisitor = OpenApi3_1SwaggerClientDereferenceVisitor({
582
+ reference,
583
+ namespace: this.namespace,
584
+ indirections: [...this.indirections],
585
+ options: this.options,
586
+ useCircularStructures: this.useCircularStructures,
587
+ allowMetaPatches: this.allowMetaPatches,
588
+ ancestors: ancestorsLineage,
589
+ basePath: (_this$basePath5 = this.basePath) != null ? _this$basePath5 : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
590
+ });
591
+ referencedElement = await visitAsync(referencedElement, mergeVisitor, {
592
+ keyMap: _apidomNsOpenapi.keyMap,
593
+ nodeTypeGetter: _apidomNsOpenapi.getNodeType
594
+ });
521
595
 
522
- // remove referencing schema from ancestors lineage
523
- directAncestors.delete(referencingElement);
596
+ // remove referencing schema from ancestors lineage
597
+ directAncestors.delete(referencingElement);
598
+ }
524
599
  this.indirections.pop();
525
600
  if ((0, _apidomNsOpenapi.isBooleanJsonSchemaElement)(referencedElement)) {
526
601
  const booleanJsonSchemaElement = (0, _apidomCore.cloneDeep)(referencedElement);
@@ -532,11 +607,20 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
532
607
  booleanJsonSchemaElement.setMetaProperty('ref-origin', reference.uri);
533
608
  // annotate fragment with info about referencing element
534
609
  booleanJsonSchemaElement.setMetaProperty('ref-referencing-element-id', (0, _apidomCore.cloneDeep)(identityManager.identify(referencingElement)));
535
- return booleanJsonSchemaElement;
610
+ if ((0, _apidomCore.isMemberElement)(parent)) {
611
+ parent.value = booleanJsonSchemaElement; // eslint-disable-line no-param-reassign
612
+ } else if (Array.isArray(parent)) {
613
+ parent[key] = booleanJsonSchemaElement; // eslint-disable-line no-param-reassign
614
+ }
615
+ return !parent ? booleanJsonSchemaElement : false;
536
616
  }
537
- const mergeAndAnnotateReferencedElement = refedElement => {
617
+
618
+ /**
619
+ * Creating a new version of Schema Object by merging fields from referenced Schema Object with referencing one.
620
+ */
621
+ if ((0, _apidomNsOpenapi.isSchemaElement)(referencedElement)) {
538
622
  // Schema Object - merge keywords from referenced schema with referencing schema
539
- const mergedElement = new _apidomNsOpenapi.SchemaElement([...refedElement.content], (0, _apidomCore.cloneDeep)(refedElement.meta), (0, _apidomCore.cloneDeep)(refedElement.attributes));
623
+ const mergedElement = new _apidomNsOpenapi.SchemaElement([...referencedElement.content], (0, _apidomCore.cloneDeep)(referencedElement.meta), (0, _apidomCore.cloneDeep)(referencedElement.attributes));
540
624
  // existing keywords from referencing schema overrides ones from referenced schema
541
625
  referencingElement.forEach((value, keyElement, item) => {
542
626
  mergedElement.remove((0, _apidomCore.toValue)(keyElement));
@@ -560,32 +644,31 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
560
644
  mergedElement.set('$$ref', baseURI);
561
645
  }
562
646
  }
563
- return mergedElement;
564
- };
647
+ referencedElement = mergedElement;
648
+ }
565
649
 
566
- // attempting to create cycle
567
- if (ancestorsLineage.includes(referencingElement) || ancestorsLineage.includes(referencedElement)) {
568
- var _ancestorsLineage$fin3;
569
- const replaceWith = (_ancestorsLineage$fin3 = ancestorsLineage.findItem(wasReferencedBy(referencingElement))) != null ? _ancestorsLineage$fin3 : mergeAndAnnotateReferencedElement(referencedElement);
570
- if ((0, _apidomCore.isMemberElement)(parent)) {
571
- parent.value = replaceWith; // eslint-disable-line no-param-reassign
572
- } else if (Array.isArray(parent)) {
573
- parent[key] = replaceWith; // eslint-disable-line no-param-reassign
574
- }
575
- return false;
650
+ /**
651
+ * Transclude referencing element with merged referenced element.
652
+ */
653
+ if ((0, _apidomCore.isMemberElement)(parent)) {
654
+ parent.value = referencedElement; // eslint-disable-line no-param-reassign
655
+ } else if (Array.isArray(parent)) {
656
+ parent[key] = referencedElement; // eslint-disable-line no-param-reassign
576
657
  }
577
658
 
578
- // transclude referencing element with merged referenced element
579
- return mergeAndAnnotateReferencedElement(referencedElement);
659
+ /**
660
+ * We're at the root of the tree, so we're just replacing the entire tree.
661
+ */
662
+ return !parent ? referencedElement : undefined;
580
663
  } catch (error) {
581
- var _this$basePath6, _this$options$derefer3;
664
+ var _this$basePath6, _this$options$derefer9;
582
665
  const rootCause = (0, _getRootCause.default)(error);
583
666
  const wrappedError = new _index.SchemaRefError(`Could not resolve reference: ${rootCause.message}`, {
584
667
  baseDoc: this.reference.uri,
585
668
  $ref: (0, _apidomCore.toValue)(referencingElement.$ref),
586
669
  fullPath: (_this$basePath6 = this.basePath) != null ? _this$basePath6 : [...(0, _toPath.default)([...ancestors, parent, referencingElement]), '$ref']
587
670
  }, rootCause);
588
- (_this$options$derefer3 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer3 = _this$options$derefer3.errors) == null || _this$options$derefer3.push == null || _this$options$derefer3.push(wrappedError);
671
+ (_this$options$derefer9 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer9 = _this$options$derefer9.errors) == null || _this$options$derefer9.push == null || _this$options$derefer9.push(wrappedError);
589
672
  return undefined;
590
673
  }
591
674
  },
@@ -601,14 +684,14 @@ const OpenApi3_1SwaggerClientDereferenceVisitor = _openapi.OpenApi3_1Dereference
601
684
  try {
602
685
  return await _openapi.OpenApi3_1DereferenceVisitor.compose.methods.ExampleElement.call(this, exampleElement, key, parent, path, ancestors);
603
686
  } catch (error) {
604
- var _this$basePath7, _this$options$derefer4;
687
+ var _this$basePath7, _this$options$derefer10;
605
688
  const rootCause = (0, _getRootCause.default)(error);
606
689
  const wrappedError = wrapError(rootCause, {
607
690
  baseDoc: this.reference.uri,
608
691
  externalValue: (0, _apidomCore.toValue)(exampleElement.externalValue),
609
692
  fullPath: (_this$basePath7 = this.basePath) != null ? _this$basePath7 : [...(0, _toPath.default)([...ancestors, parent, exampleElement]), 'externalValue']
610
693
  });
611
- (_this$options$derefer4 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer4 = _this$options$derefer4.errors) == null || _this$options$derefer4.push == null || _this$options$derefer4.push(wrappedError);
694
+ (_this$options$derefer10 = this.options.dereference.dereferenceOpts) == null || (_this$options$derefer10 = _this$options$derefer10.errors) == null || _this$options$derefer10.push == null || _this$options$derefer10.push(wrappedError);
612
695
  return undefined;
613
696
  }
614
697
  }