@speclynx/apidom-reference 6.1.0 → 6.1.2

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 (30) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/apidom-reference.browser.js +428 -318
  3. package/dist/apidom-reference.browser.min.js +1 -1
  4. package/package.json +29 -29
  5. package/src/bundle/strategies/asyncapi-2/visitor.cjs +5 -0
  6. package/src/bundle/strategies/asyncapi-2/visitor.mjs +5 -0
  7. package/src/bundle/strategies/openapi-2/visitor.cjs +4 -0
  8. package/src/bundle/strategies/openapi-2/visitor.mjs +4 -0
  9. package/src/bundle/strategies/openapi-3-0/visitor.cjs +97 -82
  10. package/src/bundle/strategies/openapi-3-0/visitor.mjs +97 -82
  11. package/src/bundle/strategies/openapi-3-1/visitor.cjs +5 -0
  12. package/src/bundle/strategies/openapi-3-1/visitor.mjs +5 -0
  13. package/src/dereference/strategies/asyncapi-2/visitor.cjs +5 -0
  14. package/src/dereference/strategies/asyncapi-2/visitor.mjs +5 -0
  15. package/src/dereference/strategies/openapi-2/visitor.cjs +10 -0
  16. package/src/dereference/strategies/openapi-2/visitor.mjs +10 -0
  17. package/src/dereference/strategies/openapi-3-0/visitor.cjs +5 -0
  18. package/src/dereference/strategies/openapi-3-0/visitor.mjs +5 -0
  19. package/src/dereference/strategies/openapi-3-1/visitor.cjs +5 -0
  20. package/src/dereference/strategies/openapi-3-1/visitor.mjs +5 -0
  21. package/src/dereference/strategies/overlay-1/extends.cjs +52 -28
  22. package/src/dereference/strategies/overlay-1/extends.mjs +52 -28
  23. package/src/dereference/strategies/overlay-1/index.cjs +22 -10
  24. package/src/dereference/strategies/overlay-1/index.mjs +23 -11
  25. package/src/dereference/strategies/overlay-1/visitor.cjs +27 -2
  26. package/src/dereference/strategies/overlay-1/visitor.mjs +27 -3
  27. package/src/parse/parsers/overlay-json-1/extends.cjs +33 -13
  28. package/src/parse/parsers/overlay-json-1/extends.mjs +33 -14
  29. package/types/bundle/strategies/openapi-3-0/visitor.d.ts +8 -3
  30. package/types/dereference/strategies/overlay-1/visitor.d.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-reference",
3
- "version": "6.1.0",
3
+ "version": "6.1.2",
4
4
  "description": "Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -299,33 +299,33 @@
299
299
  "author": "Vladimir Gorej",
300
300
  "license": "Apache-2.0",
301
301
  "dependencies": {
302
- "@babel/runtime-corejs3": "^8.0.5",
303
- "@speclynx/apidom-core": "6.1.0",
304
- "@speclynx/apidom-datamodel": "6.1.0",
305
- "@speclynx/apidom-error": "6.1.0",
306
- "@speclynx/apidom-json-pointer": "6.1.0",
307
- "@speclynx/apidom-ns-arazzo-1": "6.1.0",
308
- "@speclynx/apidom-ns-asyncapi-2": "6.1.0",
309
- "@speclynx/apidom-ns-json-schema-2020-12": "6.1.0",
310
- "@speclynx/apidom-ns-openapi-2": "6.1.0",
311
- "@speclynx/apidom-ns-openapi-3-0": "6.1.0",
312
- "@speclynx/apidom-ns-openapi-3-1": "6.1.0",
313
- "@speclynx/apidom-ns-overlay-1": "6.1.0",
314
- "@speclynx/apidom-parser-adapter-arazzo-json-1": "6.1.0",
315
- "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "6.1.0",
316
- "@speclynx/apidom-parser-adapter-asyncapi-json-2": "6.1.0",
317
- "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "6.1.0",
318
- "@speclynx/apidom-parser-adapter-json": "6.1.0",
319
- "@speclynx/apidom-parser-adapter-openapi-json-2": "6.1.0",
320
- "@speclynx/apidom-parser-adapter-openapi-json-3-0": "6.1.0",
321
- "@speclynx/apidom-parser-adapter-openapi-json-3-1": "6.1.0",
322
- "@speclynx/apidom-parser-adapter-openapi-yaml-2": "6.1.0",
323
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "6.1.0",
324
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "6.1.0",
325
- "@speclynx/apidom-parser-adapter-overlay-json-1": "6.1.0",
326
- "@speclynx/apidom-parser-adapter-overlay-yaml-1": "6.1.0",
327
- "@speclynx/apidom-parser-adapter-yaml-1-2": "6.1.0",
328
- "@speclynx/apidom-traverse": "6.1.0",
302
+ "@babel/runtime-corejs3": "^8.0.8",
303
+ "@speclynx/apidom-core": "6.1.2",
304
+ "@speclynx/apidom-datamodel": "6.1.2",
305
+ "@speclynx/apidom-error": "6.1.2",
306
+ "@speclynx/apidom-json-pointer": "6.1.2",
307
+ "@speclynx/apidom-ns-arazzo-1": "6.1.2",
308
+ "@speclynx/apidom-ns-asyncapi-2": "6.1.2",
309
+ "@speclynx/apidom-ns-json-schema-2020-12": "6.1.2",
310
+ "@speclynx/apidom-ns-openapi-2": "6.1.2",
311
+ "@speclynx/apidom-ns-openapi-3-0": "6.1.2",
312
+ "@speclynx/apidom-ns-openapi-3-1": "6.1.2",
313
+ "@speclynx/apidom-ns-overlay-1": "6.1.2",
314
+ "@speclynx/apidom-parser-adapter-arazzo-json-1": "6.1.2",
315
+ "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "6.1.2",
316
+ "@speclynx/apidom-parser-adapter-asyncapi-json-2": "6.1.2",
317
+ "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "6.1.2",
318
+ "@speclynx/apidom-parser-adapter-json": "6.1.2",
319
+ "@speclynx/apidom-parser-adapter-openapi-json-2": "6.1.2",
320
+ "@speclynx/apidom-parser-adapter-openapi-json-3-0": "6.1.2",
321
+ "@speclynx/apidom-parser-adapter-openapi-json-3-1": "6.1.2",
322
+ "@speclynx/apidom-parser-adapter-openapi-yaml-2": "6.1.2",
323
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "6.1.2",
324
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "6.1.2",
325
+ "@speclynx/apidom-parser-adapter-overlay-json-1": "6.1.2",
326
+ "@speclynx/apidom-parser-adapter-overlay-yaml-1": "6.1.2",
327
+ "@speclynx/apidom-parser-adapter-yaml-1-2": "6.1.2",
328
+ "@speclynx/apidom-traverse": "6.1.2",
329
329
  "@swaggerexpert/arazzo-runtime-expression": "^3.2.0",
330
330
  "axios": "^1.20.0",
331
331
  "picomatch": "^4.0.7",
@@ -346,5 +346,5 @@
346
346
  "README.md",
347
347
  "CHANGELOG.md"
348
348
  ],
349
- "gitHead": "575dbf59cb58a6c57248bf2c027a5cbc99442a63"
349
+ "gitHead": "e3ba735e1c56369bb263dd8246c711d405ed9606"
350
350
  }
@@ -345,6 +345,11 @@ class AsyncAPI2BundleVisitor {
345
345
  }
346
346
  async ReferenceElement(path) {
347
347
  const referencingElement = path.node;
348
+
349
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
350
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
351
+ return;
352
+ }
348
353
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
349
354
  const retrievalURI = this.toBaseURI($ref);
350
355
  const isInternalReference = this.entryURI === retrievalURI;
@@ -337,6 +337,11 @@ class AsyncAPI2BundleVisitor {
337
337
  }
338
338
  async ReferenceElement(path) {
339
339
  const referencingElement = path.node;
340
+
341
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
342
+ if (!isStringElement(referencingElement.$ref)) {
343
+ return;
344
+ }
340
345
  const $ref = toValue(referencingElement.$ref);
341
346
  const retrievalURI = this.toBaseURI($ref);
342
347
  const isInternalReference = this.entryURI === retrievalURI;
@@ -313,6 +313,10 @@ class OpenAPI2BundleVisitor {
313
313
  * and refraction helpers, so the shared logic is parameterized here.
314
314
  */
315
315
  async bundleReference(path, referencingElement, isSemanticReference, isReferenceLike, refractReferenceFn, label) {
316
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
317
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
318
+ return;
319
+ }
316
320
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
317
321
  const retrievalURI = this.toBaseURI($ref);
318
322
  const isInternalReference = this.entryURI === retrievalURI;
@@ -305,6 +305,10 @@ class OpenAPI2BundleVisitor {
305
305
  * and refraction helpers, so the shared logic is parameterized here.
306
306
  */
307
307
  async bundleReference(path, referencingElement, isSemanticReference, isReferenceLike, refractReferenceFn, label) {
308
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
309
+ if (!isStringElement(referencingElement.$ref)) {
310
+ return;
311
+ }
308
312
  const $ref = toValue(referencingElement.$ref);
309
313
  const retrievalURI = this.toBaseURI($ref);
310
314
  const isInternalReference = this.entryURI === retrievalURI;
@@ -324,6 +324,11 @@ class OpenAPI3_0BundleVisitor {
324
324
  }
325
325
  async ReferenceElement(path) {
326
326
  const referencingElement = path.node;
327
+
328
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
329
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
330
+ return;
331
+ }
327
332
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
328
333
  const retrievalURI = this.toBaseURI($ref);
329
334
  const isInternalReference = this.entryURI === retrievalURI;
@@ -459,98 +464,108 @@ class OpenAPI3_0BundleVisitor {
459
464
  * Path Item Object is not a Components Object field in OpenAPI 3.0
460
465
  * (unlike 3.1 which has `components.pathItems`). An external Path Item
461
466
  * reference therefore cannot be hoisted; it is inlined in place instead,
462
- * after bundling its own external references.
467
+ * after bundling its own external references. It is inlined on leave, once
468
+ * the sibling fields of the referencing Path Item have been bundled, so the
469
+ * traversal doesn't descend into the inlined copy and inline the `$ref` left
470
+ * in it to break a cycle again.
463
471
  */
464
- async PathItemElement(path) {
465
- const pathItemElement = path.node;
466
-
467
- // ignore Path Item Objects without a $ref field
468
- if (!(0, _apidomDatamodel.isStringElement)(pathItemElement.$ref)) {
469
- return;
470
- }
471
- const $ref = (0, _apidomCore.toValue)(pathItemElement.$ref);
472
- const retrievalURI = this.toBaseURI($ref);
473
- const isInternalReference = this.entryURI === retrievalURI;
474
- const isExternalReference = !isInternalReference;
475
- if (isInternalReference) {
476
- // normalize self-references to a bare fragment so the bundled
477
- // document stays transferable; bare fragments are left untouched
478
- pathItemElement.set('$ref', this.normalizeSelfReference($ref));
479
- return;
480
- }
481
-
482
- // honor the resolve.external switch
483
- if (!this.options.resolve.external && isExternalReference) {
484
- return;
485
- }
486
- const $refBaseURI = url.resolve(retrievalURI, $ref);
487
- const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
488
- const canonicalKey = `${retrievalURI}#${jsonPointer}`;
472
+ PathItemElement = {
473
+ leave: async path => {
474
+ const pathItemElement = path.node;
489
475
 
490
- // a circular external Path Item reference cannot be inlined without
491
- // recursing forever; leave the $ref in place to break the cycle
492
- if (this.inlineStack.has(canonicalKey)) {
493
- return;
494
- }
495
- try {
496
- // detect maximum depth of bundling
497
- if (this.reference.depth >= this.options.bundle.maxDepth) {
498
- throw new _MaximumBundleDepthError.default(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
499
- maxDepth: this.options.bundle.maxDepth,
500
- uri: this.reference.uri
501
- });
476
+ // ignore Path Item Objects without a $ref field
477
+ if (!(0, _apidomDatamodel.isStringElement)(pathItemElement.$ref)) {
478
+ return;
479
+ }
480
+ const $ref = (0, _apidomCore.toValue)(pathItemElement.$ref);
481
+ const retrievalURI = this.toBaseURI($ref);
482
+ const isInternalReference = this.entryURI === retrievalURI;
483
+ const isExternalReference = !isInternalReference;
484
+ if (isInternalReference) {
485
+ // normalize self-references to a bare fragment so the bundled
486
+ // document stays transferable; bare fragments are left untouched
487
+ pathItemElement.set('$ref', this.normalizeSelfReference($ref));
488
+ return;
502
489
  }
503
- const reference = await this.toReference($ref);
504
- let referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
505
490
 
506
- // apply Path Item semantics to the referenced fragment
507
- if (!(0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
508
- const cached = this.getRefracted(referencedElement, 'pathItem');
509
- if (cached !== undefined) {
510
- referencedElement = cached;
511
- } else {
512
- const sourceElement = referencedElement;
513
- referencedElement = (0, _apidomNsOpenapi.refractPathItem)(referencedElement);
514
- this.setRefracted(sourceElement, 'pathItem', referencedElement);
491
+ // honor the resolve.external switch
492
+ if (!this.options.resolve.external && isExternalReference) {
493
+ return;
494
+ }
495
+ const $refBaseURI = url.resolve(retrievalURI, $ref);
496
+ const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
497
+ const canonicalKey = `${retrievalURI}#${jsonPointer}`;
498
+
499
+ // a circular external Path Item reference cannot be inlined without
500
+ // recursing forever; leave the $ref in place to break the cycle. It ends
501
+ // up in the entry document, so it has to resolve from there
502
+ if (this.inlineStack.has(canonicalKey)) {
503
+ const targetURI = url.resolve(this.reference.uri, $ref);
504
+ if (url.resolve(this.entryURI, $ref) !== targetURI) {
505
+ pathItemElement.set('$ref', url.relative(this.entryURI, targetURI));
515
506
  }
507
+ return;
516
508
  }
517
-
518
- // own a copy and bundle the external references it contains
519
- const inlinedElement = (0, _apidomDatamodel.cloneDeep)(referencedElement);
520
- this.inlineStack.add(canonicalKey);
521
- const visitor = new OpenAPI3_0BundleVisitor({
522
- reference,
523
- options: this.options,
524
- assignments: this.assignments,
525
- reservedNames: this.reservedNames,
526
- refractCache: this.refractCache,
527
- inlineStack: this.inlineStack
528
- });
529
- let bundledElement;
530
509
  try {
531
- bundledElement = await (0, _apidomTraverse.traverseAsync)(inlinedElement, visitor, {
532
- mutable: true
510
+ // detect maximum depth of bundling
511
+ if (this.reference.depth >= this.options.bundle.maxDepth) {
512
+ throw new _MaximumBundleDepthError.default(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
513
+ maxDepth: this.options.bundle.maxDepth,
514
+ uri: this.reference.uri
515
+ });
516
+ }
517
+ const reference = await this.toReference($ref);
518
+ let referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
519
+
520
+ // apply Path Item semantics to the referenced fragment
521
+ if (!(0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
522
+ const cached = this.getRefracted(referencedElement, 'pathItem');
523
+ if (cached !== undefined) {
524
+ referencedElement = cached;
525
+ } else {
526
+ const sourceElement = referencedElement;
527
+ referencedElement = (0, _apidomNsOpenapi.refractPathItem)(referencedElement);
528
+ this.setRefracted(sourceElement, 'pathItem', referencedElement);
529
+ }
530
+ }
531
+
532
+ // own a copy and bundle the external references it contains
533
+ const inlinedElement = (0, _apidomDatamodel.cloneDeep)(referencedElement);
534
+ this.inlineStack.add(canonicalKey);
535
+ const visitor = new OpenAPI3_0BundleVisitor({
536
+ reference,
537
+ options: this.options,
538
+ assignments: this.assignments,
539
+ reservedNames: this.reservedNames,
540
+ refractCache: this.refractCache,
541
+ inlineStack: this.inlineStack
533
542
  });
534
- } finally {
535
- this.inlineStack.delete(canonicalKey);
536
- }
543
+ let bundledElement;
544
+ try {
545
+ bundledElement = await (0, _apidomTraverse.traverseAsync)(inlinedElement, visitor, {
546
+ mutable: true
547
+ });
548
+ } finally {
549
+ this.inlineStack.delete(canonicalKey);
550
+ }
537
551
 
538
- // merge sibling fields from the referencing Path Item over the referenced
539
- // one. The referencing `$ref` is being resolved away and is skipped; the
540
- // referenced element keeps its own `$ref` if one survived (i.e. it was
541
- // left in place to break a circular external Path Item chain).
542
- pathItemElement.forEach((_value, keyElement, item) => {
543
- const key = (0, _apidomCore.toValue)(keyElement);
544
- if (key === '$ref') return;
545
- bundledElement.remove(key);
546
- bundledElement.content.push(item);
547
- });
548
- bundledElement.meta.set('ref-origin', reference.uri);
549
- path.replaceWith(bundledElement);
550
- } catch (error) {
551
- this.handleError(`Error while bundling Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, pathItemElement, '$ref', $ref, path);
552
+ // merge sibling fields from the referencing Path Item over the referenced
553
+ // one. The referencing `$ref` is being resolved away and is skipped; the
554
+ // referenced element keeps its own `$ref` if one survived (i.e. it was
555
+ // left in place to break a circular external Path Item chain).
556
+ pathItemElement.forEach((_value, keyElement, item) => {
557
+ const key = (0, _apidomCore.toValue)(keyElement);
558
+ if (key === '$ref') return;
559
+ bundledElement.remove(key);
560
+ bundledElement.content.push(item);
561
+ });
562
+ bundledElement.meta.set('ref-origin', reference.uri);
563
+ path.replaceWith(bundledElement);
564
+ } catch (error) {
565
+ this.handleError(`Error while bundling Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, pathItemElement, '$ref', $ref, path);
566
+ }
552
567
  }
553
- }
568
+ };
554
569
 
555
570
  /**
556
571
  * Example Object `externalValue` points at arbitrary external content that
@@ -316,6 +316,11 @@ class OpenAPI3_0BundleVisitor {
316
316
  }
317
317
  async ReferenceElement(path) {
318
318
  const referencingElement = path.node;
319
+
320
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
321
+ if (!isStringElement(referencingElement.$ref)) {
322
+ return;
323
+ }
319
324
  const $ref = toValue(referencingElement.$ref);
320
325
  const retrievalURI = this.toBaseURI($ref);
321
326
  const isInternalReference = this.entryURI === retrievalURI;
@@ -451,98 +456,108 @@ class OpenAPI3_0BundleVisitor {
451
456
  * Path Item Object is not a Components Object field in OpenAPI 3.0
452
457
  * (unlike 3.1 which has `components.pathItems`). An external Path Item
453
458
  * reference therefore cannot be hoisted; it is inlined in place instead,
454
- * after bundling its own external references.
459
+ * after bundling its own external references. It is inlined on leave, once
460
+ * the sibling fields of the referencing Path Item have been bundled, so the
461
+ * traversal doesn't descend into the inlined copy and inline the `$ref` left
462
+ * in it to break a cycle again.
455
463
  */
456
- async PathItemElement(path) {
457
- const pathItemElement = path.node;
458
-
459
- // ignore Path Item Objects without a $ref field
460
- if (!isStringElement(pathItemElement.$ref)) {
461
- return;
462
- }
463
- const $ref = toValue(pathItemElement.$ref);
464
- const retrievalURI = this.toBaseURI($ref);
465
- const isInternalReference = this.entryURI === retrievalURI;
466
- const isExternalReference = !isInternalReference;
467
- if (isInternalReference) {
468
- // normalize self-references to a bare fragment so the bundled
469
- // document stays transferable; bare fragments are left untouched
470
- pathItemElement.set('$ref', this.normalizeSelfReference($ref));
471
- return;
472
- }
473
-
474
- // honor the resolve.external switch
475
- if (!this.options.resolve.external && isExternalReference) {
476
- return;
477
- }
478
- const $refBaseURI = url.resolve(retrievalURI, $ref);
479
- const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
480
- const canonicalKey = `${retrievalURI}#${jsonPointer}`;
464
+ PathItemElement = {
465
+ leave: async path => {
466
+ const pathItemElement = path.node;
481
467
 
482
- // a circular external Path Item reference cannot be inlined without
483
- // recursing forever; leave the $ref in place to break the cycle
484
- if (this.inlineStack.has(canonicalKey)) {
485
- return;
486
- }
487
- try {
488
- // detect maximum depth of bundling
489
- if (this.reference.depth >= this.options.bundle.maxDepth) {
490
- throw new MaximumBundleDepthError(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
491
- maxDepth: this.options.bundle.maxDepth,
492
- uri: this.reference.uri
493
- });
468
+ // ignore Path Item Objects without a $ref field
469
+ if (!isStringElement(pathItemElement.$ref)) {
470
+ return;
471
+ }
472
+ const $ref = toValue(pathItemElement.$ref);
473
+ const retrievalURI = this.toBaseURI($ref);
474
+ const isInternalReference = this.entryURI === retrievalURI;
475
+ const isExternalReference = !isInternalReference;
476
+ if (isInternalReference) {
477
+ // normalize self-references to a bare fragment so the bundled
478
+ // document stays transferable; bare fragments are left untouched
479
+ pathItemElement.set('$ref', this.normalizeSelfReference($ref));
480
+ return;
494
481
  }
495
- const reference = await this.toReference($ref);
496
- let referencedElement = evaluate(reference.value.result, jsonPointer);
497
482
 
498
- // apply Path Item semantics to the referenced fragment
499
- if (!isPathItemElement(referencedElement)) {
500
- const cached = this.getRefracted(referencedElement, 'pathItem');
501
- if (cached !== undefined) {
502
- referencedElement = cached;
503
- } else {
504
- const sourceElement = referencedElement;
505
- referencedElement = refractPathItem(referencedElement);
506
- this.setRefracted(sourceElement, 'pathItem', referencedElement);
483
+ // honor the resolve.external switch
484
+ if (!this.options.resolve.external && isExternalReference) {
485
+ return;
486
+ }
487
+ const $refBaseURI = url.resolve(retrievalURI, $ref);
488
+ const jsonPointer = URIFragmentIdentifier.fromURIReference($refBaseURI);
489
+ const canonicalKey = `${retrievalURI}#${jsonPointer}`;
490
+
491
+ // a circular external Path Item reference cannot be inlined without
492
+ // recursing forever; leave the $ref in place to break the cycle. It ends
493
+ // up in the entry document, so it has to resolve from there
494
+ if (this.inlineStack.has(canonicalKey)) {
495
+ const targetURI = url.resolve(this.reference.uri, $ref);
496
+ if (url.resolve(this.entryURI, $ref) !== targetURI) {
497
+ pathItemElement.set('$ref', url.relative(this.entryURI, targetURI));
507
498
  }
499
+ return;
508
500
  }
509
-
510
- // own a copy and bundle the external references it contains
511
- const inlinedElement = cloneDeep(referencedElement);
512
- this.inlineStack.add(canonicalKey);
513
- const visitor = new OpenAPI3_0BundleVisitor({
514
- reference,
515
- options: this.options,
516
- assignments: this.assignments,
517
- reservedNames: this.reservedNames,
518
- refractCache: this.refractCache,
519
- inlineStack: this.inlineStack
520
- });
521
- let bundledElement;
522
501
  try {
523
- bundledElement = await traverseAsync(inlinedElement, visitor, {
524
- mutable: true
502
+ // detect maximum depth of bundling
503
+ if (this.reference.depth >= this.options.bundle.maxDepth) {
504
+ throw new MaximumBundleDepthError(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
505
+ maxDepth: this.options.bundle.maxDepth,
506
+ uri: this.reference.uri
507
+ });
508
+ }
509
+ const reference = await this.toReference($ref);
510
+ let referencedElement = evaluate(reference.value.result, jsonPointer);
511
+
512
+ // apply Path Item semantics to the referenced fragment
513
+ if (!isPathItemElement(referencedElement)) {
514
+ const cached = this.getRefracted(referencedElement, 'pathItem');
515
+ if (cached !== undefined) {
516
+ referencedElement = cached;
517
+ } else {
518
+ const sourceElement = referencedElement;
519
+ referencedElement = refractPathItem(referencedElement);
520
+ this.setRefracted(sourceElement, 'pathItem', referencedElement);
521
+ }
522
+ }
523
+
524
+ // own a copy and bundle the external references it contains
525
+ const inlinedElement = cloneDeep(referencedElement);
526
+ this.inlineStack.add(canonicalKey);
527
+ const visitor = new OpenAPI3_0BundleVisitor({
528
+ reference,
529
+ options: this.options,
530
+ assignments: this.assignments,
531
+ reservedNames: this.reservedNames,
532
+ refractCache: this.refractCache,
533
+ inlineStack: this.inlineStack
525
534
  });
526
- } finally {
527
- this.inlineStack.delete(canonicalKey);
528
- }
535
+ let bundledElement;
536
+ try {
537
+ bundledElement = await traverseAsync(inlinedElement, visitor, {
538
+ mutable: true
539
+ });
540
+ } finally {
541
+ this.inlineStack.delete(canonicalKey);
542
+ }
529
543
 
530
- // merge sibling fields from the referencing Path Item over the referenced
531
- // one. The referencing `$ref` is being resolved away and is skipped; the
532
- // referenced element keeps its own `$ref` if one survived (i.e. it was
533
- // left in place to break a circular external Path Item chain).
534
- pathItemElement.forEach((_value, keyElement, item) => {
535
- const key = toValue(keyElement);
536
- if (key === '$ref') return;
537
- bundledElement.remove(key);
538
- bundledElement.content.push(item);
539
- });
540
- bundledElement.meta.set('ref-origin', reference.uri);
541
- path.replaceWith(bundledElement);
542
- } catch (error) {
543
- this.handleError(`Error while bundling Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, pathItemElement, '$ref', $ref, path);
544
+ // merge sibling fields from the referencing Path Item over the referenced
545
+ // one. The referencing `$ref` is being resolved away and is skipped; the
546
+ // referenced element keeps its own `$ref` if one survived (i.e. it was
547
+ // left in place to break a circular external Path Item chain).
548
+ pathItemElement.forEach((_value, keyElement, item) => {
549
+ const key = toValue(keyElement);
550
+ if (key === '$ref') return;
551
+ bundledElement.remove(key);
552
+ bundledElement.content.push(item);
553
+ });
554
+ bundledElement.meta.set('ref-origin', reference.uri);
555
+ path.replaceWith(bundledElement);
556
+ } catch (error) {
557
+ this.handleError(`Error while bundling Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, pathItemElement, '$ref', $ref, path);
558
+ }
544
559
  }
545
- }
560
+ };
546
561
 
547
562
  /**
548
563
  * Example Object `externalValue` points at arbitrary external content that
@@ -422,6 +422,11 @@ class OpenAPI3_1BundleVisitor {
422
422
  };
423
423
  async ReferenceElement(path) {
424
424
  const referencingElement = path.node;
425
+
426
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
427
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
428
+ return;
429
+ }
425
430
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
426
431
  const retrievalURI = this.toBaseURI($ref);
427
432
  const isInternalReference = this.entryURI === retrievalURI;
@@ -414,6 +414,11 @@ class OpenAPI3_1BundleVisitor {
414
414
  };
415
415
  async ReferenceElement(path) {
416
416
  const referencingElement = path.node;
417
+
418
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
419
+ if (!isStringElement(referencingElement.$ref)) {
420
+ return;
421
+ }
417
422
  const $ref = toValue(referencingElement.$ref);
418
423
  const retrievalURI = this.toBaseURI($ref);
419
424
  const isInternalReference = this.entryURI === retrievalURI;
@@ -202,6 +202,11 @@ class AsyncAPI2DereferenceVisitor {
202
202
  async ReferenceElement(path) {
203
203
  const referencingElement = path.node;
204
204
 
205
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
206
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
207
+ return;
208
+ }
209
+
205
210
  // skip current referencing element as it's already been access
206
211
  if (this.indirections.includes(referencingElement)) {
207
212
  path.skip();
@@ -193,6 +193,11 @@ class AsyncAPI2DereferenceVisitor {
193
193
  async ReferenceElement(path) {
194
194
  const referencingElement = path.node;
195
195
 
196
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
197
+ if (!isStringElement(referencingElement.$ref)) {
198
+ return;
199
+ }
200
+
196
201
  // skip current referencing element as it's already been access
197
202
  if (this.indirections.includes(referencingElement)) {
198
203
  path.skip();
@@ -202,6 +202,11 @@ class OpenAPI2DereferenceVisitor {
202
202
  async ReferenceElement(path) {
203
203
  const referencingElement = path.node;
204
204
 
205
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
206
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
207
+ return;
208
+ }
209
+
205
210
  // skip current referencing element as it's already been access
206
211
  if (this.indirections.includes(referencingElement)) {
207
212
  path.skip();
@@ -505,6 +510,11 @@ class OpenAPI2DereferenceVisitor {
505
510
  async JSONReferenceElement(path) {
506
511
  const referencingElement = path.node;
507
512
 
513
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
514
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
515
+ return;
516
+ }
517
+
508
518
  // skip current referencing element as it's already been access
509
519
  if (this.indirections.includes(referencingElement)) {
510
520
  path.skip();
@@ -193,6 +193,11 @@ class OpenAPI2DereferenceVisitor {
193
193
  async ReferenceElement(path) {
194
194
  const referencingElement = path.node;
195
195
 
196
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
197
+ if (!isStringElement(referencingElement.$ref)) {
198
+ return;
199
+ }
200
+
196
201
  // skip current referencing element as it's already been access
197
202
  if (this.indirections.includes(referencingElement)) {
198
203
  path.skip();
@@ -496,6 +501,11 @@ class OpenAPI2DereferenceVisitor {
496
501
  async JSONReferenceElement(path) {
497
502
  const referencingElement = path.node;
498
503
 
504
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
505
+ if (!isStringElement(referencingElement.$ref)) {
506
+ return;
507
+ }
508
+
499
509
  // skip current referencing element as it's already been access
500
510
  if (this.indirections.includes(referencingElement)) {
501
511
  path.skip();
@@ -203,6 +203,11 @@ class OpenAPI3_0DereferenceVisitor {
203
203
  async ReferenceElement(path) {
204
204
  const referencingElement = path.node;
205
205
 
206
+ // ignore Reference Object without a string $ref, e.g. an empty YAML value
207
+ if (!(0, _apidomDatamodel.isStringElement)(referencingElement.$ref)) {
208
+ return;
209
+ }
210
+
206
211
  // skip current referencing element as it's already been access
207
212
  if (this.indirections.includes(referencingElement)) {
208
213
  path.skip();