@speclynx/apidom-reference 5.2.4 → 5.2.5
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.
- package/CHANGELOG.md +12 -0
- package/README.md +30 -11
- package/dist/apidom-reference.browser.js +1121 -514
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +28 -28
- package/src/bundle/strategies/arazzo-1/visitor.cjs +105 -22
- package/src/bundle/strategies/arazzo-1/visitor.mjs +107 -24
- package/src/bundle/strategies/openapi-3-1/visitor.cjs +197 -47
- package/src/bundle/strategies/openapi-3-1/visitor.mjs +200 -50
- package/src/bundle/util.cjs +57 -2
- package/src/bundle/util.mjs +52 -0
- package/src/dereference/strategies/arazzo-1/index.cjs +9 -7
- package/src/dereference/strategies/arazzo-1/index.mjs +3 -1
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/util.cjs +6 -0
- package/src/dereference/strategies/arazzo-1/util.mjs +1 -1
- package/src/dereference/strategies/arazzo-1/visitor.cjs +67 -9
- package/src/dereference/strategies/arazzo-1/visitor.mjs +71 -13
- package/src/dereference/strategies/openapi-3-1/index.cjs +2 -1
- package/src/dereference/strategies/openapi-3-1/index.mjs +3 -1
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +16 -4
- package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +14 -3
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.cjs +29 -0
- package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.mjs +22 -0
- package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +54 -27
- package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +51 -27
- package/src/dereference/strategies/openapi-3-1/util.cjs +75 -14
- package/src/dereference/strategies/openapi-3-1/util.mjs +68 -14
- package/src/dereference/strategies/openapi-3-1/visitor.cjs +85 -9
- package/src/dereference/strategies/openapi-3-1/visitor.mjs +89 -13
- package/src/util/url.cjs +35 -2
- package/src/util/url.mjs +32 -0
- package/types/apidom-reference.d.ts +12 -0
- package/types/bundle/strategies/arazzo-1/index.d.ts +1 -1
- package/types/bundle/strategies/arazzo-1/visitor.d.ts +51 -6
- package/types/bundle/strategies/openapi-3-1/index.d.ts +1 -1
- package/types/bundle/strategies/openapi-3-1/visitor.d.ts +59 -3
- package/types/bundle/util.d.ts +39 -0
- package/types/dereference/strategies/arazzo-1/selectors/$anchor.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/selectors/uri.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/util.d.ts +1 -1
- package/types/dereference/strategies/arazzo-1/visitor.d.ts +17 -1
- package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +8 -0
- package/types/dereference/strategies/openapi-3-1/selectors/json-pointer.d.ts +9 -0
- package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +37 -4
- package/types/dereference/strategies/openapi-3-1/util.d.ts +53 -2
- package/types/dereference/strategies/openapi-3-1/visitor.d.ts +17 -1
- package/types/util/url.d.ts +10 -0
|
@@ -5,7 +5,7 @@ import { isSwaggerElement } from '@speclynx/apidom-ns-openapi-2';
|
|
|
5
5
|
import { isOpenApi3_0Element } from '@speclynx/apidom-ns-openapi-3-0';
|
|
6
6
|
import { isOpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
|
|
7
7
|
import * as url from "../../../util/url.mjs";
|
|
8
|
-
export { resolveSchema$refField, resolveSchema$idField } from "../openapi-3-1/util.mjs";
|
|
8
|
+
export { resolveSchema$refField, resolveSchema$idField, resolveSchemaBaseURI } from "../openapi-3-1/util.mjs";
|
|
9
9
|
/**
|
|
10
10
|
* Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
|
|
11
11
|
*
|
|
@@ -16,6 +16,7 @@ var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
|
|
|
16
16
|
var _arazzoRuntimeExpression = require("@swaggerexpert/arazzo-runtime-expression");
|
|
17
17
|
var _$anchor = require("./selectors/$anchor.cjs");
|
|
18
18
|
var _uri = require("./selectors/uri.cjs");
|
|
19
|
+
var _jsonPointer = require("../openapi-3-1/selectors/json-pointer.cjs");
|
|
19
20
|
var _util = require("../openapi-3-1/util.cjs");
|
|
20
21
|
var _util2 = require("./util.cjs");
|
|
21
22
|
var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
|
|
@@ -45,8 +46,23 @@ class Arazzo1DereferenceVisitor {
|
|
|
45
46
|
*/
|
|
46
47
|
baseURI;
|
|
47
48
|
options;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Index of `$id`-bearing schemas per document, shared across nested visitors
|
|
52
|
+
* of a single dereference run so it never outlives mutations of the documents.
|
|
53
|
+
*/
|
|
54
|
+
schema$idIndex;
|
|
48
55
|
visited;
|
|
49
56
|
|
|
57
|
+
/**
|
|
58
|
+
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
59
|
+
* document (outermost first). Empty when the root is the document itself; a
|
|
60
|
+
* nested visitor traversing a fragment is seeded with the `$id`s of the
|
|
61
|
+
* schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
|
|
62
|
+
* against the same base as they would from the document root.
|
|
63
|
+
*/
|
|
64
|
+
ancestorSchema$ids;
|
|
65
|
+
|
|
50
66
|
/**
|
|
51
67
|
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
52
68
|
* Used for cycle detection: if a referenced element is found in
|
|
@@ -57,15 +73,19 @@ class Arazzo1DereferenceVisitor {
|
|
|
57
73
|
reference,
|
|
58
74
|
options,
|
|
59
75
|
indirections = [],
|
|
76
|
+
schema$idIndex = new WeakMap(),
|
|
60
77
|
ancestors = new _util3.AncestorLineage(),
|
|
61
|
-
visited = new WeakSet()
|
|
78
|
+
visited = new WeakSet(),
|
|
79
|
+
ancestorSchema$ids = []
|
|
62
80
|
}) {
|
|
63
81
|
this.indirections = indirections;
|
|
64
82
|
this.reference = reference;
|
|
65
83
|
this.baseURI = (0, _util2.resolveArazzo$selfField)(reference.uri, reference.value?.result);
|
|
66
84
|
this.options = options;
|
|
85
|
+
this.schema$idIndex = schema$idIndex;
|
|
67
86
|
this.ancestors = new _util3.AncestorLineage(...ancestors);
|
|
68
87
|
this.visited = visited;
|
|
88
|
+
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
69
89
|
}
|
|
70
90
|
toAncestorLineage(path) {
|
|
71
91
|
const ancestorNodes = path.getAncestorNodes();
|
|
@@ -307,7 +327,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
307
327
|
let {
|
|
308
328
|
uri: retrievalURI
|
|
309
329
|
} = reference;
|
|
310
|
-
|
|
330
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
331
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
332
|
+
const rootBaseURI = (0, _util.resolveSchema$ids)(this.baseURI, this.ancestorSchema$ids);
|
|
333
|
+
const schemaBaseURI = (0, _util.resolveSchemaBaseURI)(rootBaseURI, path);
|
|
334
|
+
const $refBaseURI = (0, _util.resolveSchema$refField)(schemaBaseURI, referencingElement);
|
|
311
335
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
312
336
|
const file = new _File.default({
|
|
313
337
|
uri: $refBaseURIStrippedHash
|
|
@@ -319,13 +343,32 @@ class Arazzo1DereferenceVisitor {
|
|
|
319
343
|
|
|
320
344
|
// determining reference, proper evaluation and selection mechanism
|
|
321
345
|
let referencedElement;
|
|
346
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
347
|
+
let ancestorSchema$ids;
|
|
322
348
|
try {
|
|
323
349
|
if (isUnknownURI || isURL) {
|
|
324
350
|
// we're dealing with canonical URI or URL with possible fragment
|
|
325
351
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
326
352
|
const selector = $refBaseURI;
|
|
327
|
-
const
|
|
328
|
-
|
|
353
|
+
const documentElement = reference.value.result;
|
|
354
|
+
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(documentElement);
|
|
355
|
+
// a document that is itself a fragment detached from its source carries
|
|
356
|
+
// the $ids enclosing it only as `ancestorSchema$ids`: its $ids are then
|
|
357
|
+
// indexed against the base that chain leaves in effect, and the chain
|
|
358
|
+
// prefixes the one of the located schema. A nested visitor rooted at a
|
|
359
|
+
// fragment of a whole document indexes that document against its own base.
|
|
360
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
361
|
+
const isDocumentRoot = ((0, _apidomDatamodel.isParseResultElement)(rootNode) ? rootNode.result : rootNode) === documentElement;
|
|
362
|
+
({
|
|
363
|
+
element: referencedElement,
|
|
364
|
+
ancestorSchema$ids
|
|
365
|
+
} = (0, _uri.locate)(selector, referenceAsSchema, {
|
|
366
|
+
baseURI: isDocumentRoot ? rootBaseURI : this.baseURI,
|
|
367
|
+
index: this.schema$idIndex
|
|
368
|
+
}));
|
|
369
|
+
if (isDocumentRoot) {
|
|
370
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
371
|
+
}
|
|
329
372
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
330
373
|
|
|
331
374
|
// ignore resolving internal Schema Objects
|
|
@@ -357,7 +400,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
357
400
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
358
401
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
359
402
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
360
|
-
|
|
403
|
+
({
|
|
404
|
+
element: referencedElement,
|
|
405
|
+
ancestorSchema$ids
|
|
406
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
361
407
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
362
408
|
}
|
|
363
409
|
} catch (error) {
|
|
@@ -384,7 +430,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
384
430
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
385
431
|
const selector = (0, _$anchor.uriToAnchor)($refBaseURI);
|
|
386
432
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
387
|
-
|
|
433
|
+
({
|
|
434
|
+
element: referencedElement,
|
|
435
|
+
ancestorSchema$ids
|
|
436
|
+
} = (0, _$anchor.locate)(selector, referenceAsSchema));
|
|
388
437
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
389
438
|
} else {
|
|
390
439
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -405,7 +454,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
405
454
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
406
455
|
const selector = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
407
456
|
const referenceAsSchema = (0, _util2.maybeRefractToJSONSchemaElement)(reference.value.result);
|
|
408
|
-
|
|
457
|
+
({
|
|
458
|
+
element: referencedElement,
|
|
459
|
+
ancestorSchema$ids
|
|
460
|
+
} = (0, _jsonPointer.locate)(referenceAsSchema, selector));
|
|
409
461
|
referencedElement = (0, _util2.maybeRefractToJSONSchemaElement)(referencedElement);
|
|
410
462
|
}
|
|
411
463
|
} else {
|
|
@@ -462,7 +514,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
462
514
|
*/
|
|
463
515
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
464
516
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
465
|
-
|
|
517
|
+
// content transplanted into the referencing site is traversed under that
|
|
518
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
519
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
520
|
+
const hasDifferentEnclosingBase = (0, _util.resolveSchema$ids)((0, _util2.resolveArazzo$selfField)(reference.uri, reference.value.result), ancestorSchema$ids) !== (0, _util.resolveSchema$ids)(rootBaseURI, (0, _util.schema$idsOf)(path.getAncestorNodes().reverse()));
|
|
521
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || (0, _apidomNsArazzo.isJSONSchemaElement)(referencedElement) && (0, _apidomDatamodel.isStringElement)(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
466
522
|
if (shouldDetectCircular) {
|
|
467
523
|
this.visited.add(referencedElement);
|
|
468
524
|
}
|
|
@@ -473,8 +529,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
473
529
|
reference,
|
|
474
530
|
indirections: [...this.indirections],
|
|
475
531
|
options: this.options,
|
|
532
|
+
schema$idIndex: this.schema$idIndex,
|
|
476
533
|
visited: this.visited,
|
|
477
|
-
ancestors: ancestorsLineage
|
|
534
|
+
ancestors: ancestorsLineage,
|
|
535
|
+
ancestorSchema$ids
|
|
478
536
|
});
|
|
479
537
|
referencedElement = await (0, _apidomTraverse.traverseAsync)(referencedElement, visitor, {
|
|
480
538
|
mutable: true,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { propEq, none } from 'ramda';
|
|
2
|
-
import { isElement, isStringElement, RefElement, cloneShallow, cloneDeep } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import { isElement, isStringElement, isParseResultElement, RefElement, cloneShallow, cloneDeep } from '@speclynx/apidom-datamodel';
|
|
3
3
|
import { toValue, toYAML } from '@speclynx/apidom-core';
|
|
4
4
|
import { ApiDOMStructuredError } from '@speclynx/apidom-error';
|
|
5
5
|
import { traverse, traverseAsync } from '@speclynx/apidom-traverse';
|
|
6
6
|
import { evaluate as jsonPointerEvaluate, compile as jsonPointerCompile, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
|
|
7
7
|
import { isParameterElement, isJSONSchemaElement, isBooleanJSONSchemaElement } from '@speclynx/apidom-ns-arazzo-1';
|
|
8
8
|
import { parse as parseRuntimeExpression } from '@swaggerexpert/arazzo-runtime-expression';
|
|
9
|
-
import { isAnchor, uriToAnchor,
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { isAnchor, uriToAnchor, locate as $anchorLocate } from "./selectors/$anchor.mjs";
|
|
10
|
+
import { locate as uriLocate } from "./selectors/uri.mjs";
|
|
11
|
+
import { locate as jsonPointerLocate } from "../openapi-3-1/selectors/json-pointer.mjs";
|
|
12
|
+
import { schema$idsOf, resolveSchema$ids, resolveSchemaBaseURI, resolveSchema$refField } from "../openapi-3-1/util.mjs";
|
|
12
13
|
import { maybeRefractToJSONSchemaElement, resolveArazzo$selfField, identifiedBy$self } from "./util.mjs";
|
|
13
14
|
import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
|
|
14
15
|
import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
|
|
@@ -36,8 +37,23 @@ class Arazzo1DereferenceVisitor {
|
|
|
36
37
|
*/
|
|
37
38
|
baseURI;
|
|
38
39
|
options;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Index of `$id`-bearing schemas per document, shared across nested visitors
|
|
43
|
+
* of a single dereference run so it never outlives mutations of the documents.
|
|
44
|
+
*/
|
|
45
|
+
schema$idIndex;
|
|
39
46
|
visited;
|
|
40
47
|
|
|
48
|
+
/**
|
|
49
|
+
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
50
|
+
* document (outermost first). Empty when the root is the document itself; a
|
|
51
|
+
* nested visitor traversing a fragment is seeded with the `$id`s of the
|
|
52
|
+
* schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
|
|
53
|
+
* against the same base as they would from the document root.
|
|
54
|
+
*/
|
|
55
|
+
ancestorSchema$ids;
|
|
56
|
+
|
|
41
57
|
/**
|
|
42
58
|
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
43
59
|
* Used for cycle detection: if a referenced element is found in
|
|
@@ -48,15 +64,19 @@ class Arazzo1DereferenceVisitor {
|
|
|
48
64
|
reference,
|
|
49
65
|
options,
|
|
50
66
|
indirections = [],
|
|
67
|
+
schema$idIndex = new WeakMap(),
|
|
51
68
|
ancestors = new AncestorLineage(),
|
|
52
|
-
visited = new WeakSet()
|
|
69
|
+
visited = new WeakSet(),
|
|
70
|
+
ancestorSchema$ids = []
|
|
53
71
|
}) {
|
|
54
72
|
this.indirections = indirections;
|
|
55
73
|
this.reference = reference;
|
|
56
74
|
this.baseURI = resolveArazzo$selfField(reference.uri, reference.value?.result);
|
|
57
75
|
this.options = options;
|
|
76
|
+
this.schema$idIndex = schema$idIndex;
|
|
58
77
|
this.ancestors = new AncestorLineage(...ancestors);
|
|
59
78
|
this.visited = visited;
|
|
79
|
+
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
60
80
|
}
|
|
61
81
|
toAncestorLineage(path) {
|
|
62
82
|
const ancestorNodes = path.getAncestorNodes();
|
|
@@ -298,7 +318,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
298
318
|
let {
|
|
299
319
|
uri: retrievalURI
|
|
300
320
|
} = reference;
|
|
301
|
-
|
|
321
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
322
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
323
|
+
const rootBaseURI = resolveSchema$ids(this.baseURI, this.ancestorSchema$ids);
|
|
324
|
+
const schemaBaseURI = resolveSchemaBaseURI(rootBaseURI, path);
|
|
325
|
+
const $refBaseURI = resolveSchema$refField(schemaBaseURI, referencingElement);
|
|
302
326
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
303
327
|
const file = new File({
|
|
304
328
|
uri: $refBaseURIStrippedHash
|
|
@@ -310,13 +334,32 @@ class Arazzo1DereferenceVisitor {
|
|
|
310
334
|
|
|
311
335
|
// determining reference, proper evaluation and selection mechanism
|
|
312
336
|
let referencedElement;
|
|
337
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
338
|
+
let ancestorSchema$ids;
|
|
313
339
|
try {
|
|
314
340
|
if (isUnknownURI || isURL) {
|
|
315
341
|
// we're dealing with canonical URI or URL with possible fragment
|
|
316
342
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
317
343
|
const selector = $refBaseURI;
|
|
318
|
-
const
|
|
319
|
-
|
|
344
|
+
const documentElement = reference.value.result;
|
|
345
|
+
const referenceAsSchema = maybeRefractToJSONSchemaElement(documentElement);
|
|
346
|
+
// a document that is itself a fragment detached from its source carries
|
|
347
|
+
// the $ids enclosing it only as `ancestorSchema$ids`: its $ids are then
|
|
348
|
+
// indexed against the base that chain leaves in effect, and the chain
|
|
349
|
+
// prefixes the one of the located schema. A nested visitor rooted at a
|
|
350
|
+
// fragment of a whole document indexes that document against its own base.
|
|
351
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
352
|
+
const isDocumentRoot = (isParseResultElement(rootNode) ? rootNode.result : rootNode) === documentElement;
|
|
353
|
+
({
|
|
354
|
+
element: referencedElement,
|
|
355
|
+
ancestorSchema$ids
|
|
356
|
+
} = uriLocate(selector, referenceAsSchema, {
|
|
357
|
+
baseURI: isDocumentRoot ? rootBaseURI : this.baseURI,
|
|
358
|
+
index: this.schema$idIndex
|
|
359
|
+
}));
|
|
360
|
+
if (isDocumentRoot) {
|
|
361
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
362
|
+
}
|
|
320
363
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
321
364
|
|
|
322
365
|
// ignore resolving internal Schema Objects
|
|
@@ -348,7 +391,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
348
391
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
349
392
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
350
393
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
351
|
-
|
|
394
|
+
({
|
|
395
|
+
element: referencedElement,
|
|
396
|
+
ancestorSchema$ids
|
|
397
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
352
398
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
353
399
|
}
|
|
354
400
|
} catch (error) {
|
|
@@ -375,7 +421,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
375
421
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
376
422
|
const selector = uriToAnchor($refBaseURI);
|
|
377
423
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
378
|
-
|
|
424
|
+
({
|
|
425
|
+
element: referencedElement,
|
|
426
|
+
ancestorSchema$ids
|
|
427
|
+
} = $anchorLocate(selector, referenceAsSchema));
|
|
379
428
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
380
429
|
} else {
|
|
381
430
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -396,7 +445,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
396
445
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
397
446
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
398
447
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
399
|
-
|
|
448
|
+
({
|
|
449
|
+
element: referencedElement,
|
|
450
|
+
ancestorSchema$ids
|
|
451
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
400
452
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
401
453
|
}
|
|
402
454
|
} else {
|
|
@@ -453,7 +505,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
453
505
|
*/
|
|
454
506
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
455
507
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
456
|
-
|
|
508
|
+
// content transplanted into the referencing site is traversed under that
|
|
509
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
510
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
511
|
+
const hasDifferentEnclosingBase = resolveSchema$ids(resolveArazzo$selfField(reference.uri, reference.value.result), ancestorSchema$ids) !== resolveSchema$ids(rootBaseURI, schema$idsOf(path.getAncestorNodes().reverse()));
|
|
512
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || isJSONSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
457
513
|
if (shouldDetectCircular) {
|
|
458
514
|
this.visited.add(referencedElement);
|
|
459
515
|
}
|
|
@@ -464,8 +520,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
464
520
|
reference,
|
|
465
521
|
indirections: [...this.indirections],
|
|
466
522
|
options: this.options,
|
|
523
|
+
schema$idIndex: this.schema$idIndex,
|
|
467
524
|
visited: this.visited,
|
|
468
|
-
ancestors: ancestorsLineage
|
|
525
|
+
ancestors: ancestorsLineage,
|
|
526
|
+
ancestorSchema$ids
|
|
469
527
|
});
|
|
470
528
|
referencedElement = await traverseAsync(referencedElement, visitor, {
|
|
471
529
|
mutable: true,
|
|
@@ -92,7 +92,8 @@ class OpenAPI3_1DereferenceStrategy extends _DereferenceStrategy.default {
|
|
|
92
92
|
const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
|
|
93
93
|
const visitor = new _visitor.default({
|
|
94
94
|
reference: reference,
|
|
95
|
-
options
|
|
95
|
+
options,
|
|
96
|
+
ancestorSchema$ids: (0, _util.detachedRootSchema$ids)(refSet.rootRef.value.result)
|
|
96
97
|
});
|
|
97
98
|
const dereferencedElement = await (0, _apidomTraverse.traverseAsync)(refSet.rootRef.value, visitor, {
|
|
98
99
|
mutable: true,
|
|
@@ -6,6 +6,7 @@ import DereferenceStrategy from "../DereferenceStrategy.mjs";
|
|
|
6
6
|
import Reference from "../../../Reference.mjs";
|
|
7
7
|
import ReferenceSet from "../../../ReferenceSet.mjs";
|
|
8
8
|
import OpenAPI3_1DereferenceVisitor from "./visitor.mjs";
|
|
9
|
+
import { detachedRootSchema$ids } from "./util.mjs";
|
|
9
10
|
/**
|
|
10
11
|
* @public
|
|
11
12
|
*/
|
|
@@ -59,7 +60,8 @@ class OpenAPI3_1DereferenceStrategy extends DereferenceStrategy {
|
|
|
59
60
|
const shouldDetectCircular = ['error', 'replace'].includes(options.dereference.circular);
|
|
60
61
|
const visitor = new OpenAPI3_1DereferenceVisitor({
|
|
61
62
|
reference: reference,
|
|
62
|
-
options
|
|
63
|
+
options,
|
|
64
|
+
ancestorSchema$ids: detachedRootSchema$ids(refSet.rootRef.value.result)
|
|
63
65
|
});
|
|
64
66
|
const dereferencedElement = await traverseAsync(refSet.rootRef.value, visitor, {
|
|
65
67
|
mutable: true,
|
|
@@ -22,12 +22,13 @@ Object.defineProperty(exports, "JsonSchema$anchorError", {
|
|
|
22
22
|
return _JsonSchema$anchorError.default;
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
exports.uriToAnchor = exports.parse = exports.isAnchor = exports.evaluate = void 0;
|
|
25
|
+
exports.uriToAnchor = exports.parse = exports.locate = exports.isAnchor = exports.evaluate = void 0;
|
|
26
26
|
var _ramdaAdjunct = require("ramda-adjunct");
|
|
27
27
|
var _apidomCore = require("@speclynx/apidom-core");
|
|
28
28
|
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
29
29
|
var _apidomNsJsonSchema = require("@speclynx/apidom-ns-json-schema-2020-12");
|
|
30
30
|
var _url = require("../../../../util/url.cjs");
|
|
31
|
+
var _util = require("../util.cjs");
|
|
31
32
|
var _EvaluationJsonSchema$anchorError = _interopRequireDefault(require("../../../../errors/EvaluationJsonSchema$anchorError.cjs"));
|
|
32
33
|
var _InvalidJsonSchema$anchorError = _interopRequireDefault(require("../../../../errors/InvalidJsonSchema$anchorError.cjs"));
|
|
33
34
|
var _JsonSchema$anchorError = _interopRequireDefault(require("../../../../errors/JsonSchema$anchorError.cjs"));
|
|
@@ -65,11 +66,12 @@ const parse = anchor => {
|
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
|
-
*
|
|
69
|
+
* Locates the schema identified by JSON Schema $anchor within ApiDOM fragment,
|
|
70
|
+
* along with the `$id`s of the schemas enclosing it, the search root included.
|
|
69
71
|
* @public
|
|
70
72
|
*/
|
|
71
73
|
exports.parse = parse;
|
|
72
|
-
const
|
|
74
|
+
const locate = (anchor, element) => {
|
|
73
75
|
const token = parse(anchor);
|
|
74
76
|
const resultPath = (0, _apidomTraverse.find)(element,
|
|
75
77
|
// @ts-ignore
|
|
@@ -77,6 +79,16 @@ const evaluate = (anchor, element) => {
|
|
|
77
79
|
if ((0, _ramdaAdjunct.isUndefined)(resultPath)) {
|
|
78
80
|
throw new _EvaluationJsonSchema$anchorError.default(`Evaluation failed on token: "${token}"`);
|
|
79
81
|
}
|
|
80
|
-
return
|
|
82
|
+
return {
|
|
83
|
+
element: resultPath.node,
|
|
84
|
+
ancestorSchema$ids: (0, _util.schema$idsOf)(resultPath.getAncestorNodes().reverse())
|
|
85
|
+
};
|
|
81
86
|
};
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Evaluates JSON Schema $anchor against ApiDOM fragment.
|
|
90
|
+
* @public
|
|
91
|
+
*/
|
|
92
|
+
exports.locate = locate;
|
|
93
|
+
const evaluate = (anchor, element) => locate(anchor, element).element;
|
|
82
94
|
exports.evaluate = evaluate;
|
|
@@ -3,6 +3,7 @@ import { toValue } from '@speclynx/apidom-core';
|
|
|
3
3
|
import { find } from '@speclynx/apidom-traverse';
|
|
4
4
|
import { isJSONSchemaElement } from '@speclynx/apidom-ns-json-schema-2020-12';
|
|
5
5
|
import { getHash } from "../../../../util/url.mjs";
|
|
6
|
+
import { schema$idsOf } from "../util.mjs";
|
|
6
7
|
import EvaluationJsonSchema$anchorError from "../../../../errors/EvaluationJsonSchema$anchorError.mjs";
|
|
7
8
|
import InvalidJsonSchema$anchorError from "../../../../errors/InvalidJsonSchema$anchorError.mjs";
|
|
8
9
|
/**
|
|
@@ -37,10 +38,11 @@ export const parse = anchor => {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
|
-
*
|
|
41
|
+
* Locates the schema identified by JSON Schema $anchor within ApiDOM fragment,
|
|
42
|
+
* along with the `$id`s of the schemas enclosing it, the search root included.
|
|
41
43
|
* @public
|
|
42
44
|
*/
|
|
43
|
-
export const
|
|
45
|
+
export const locate = (anchor, element) => {
|
|
44
46
|
const token = parse(anchor);
|
|
45
47
|
const resultPath = find(element,
|
|
46
48
|
// @ts-ignore
|
|
@@ -48,7 +50,16 @@ export const evaluate = (anchor, element) => {
|
|
|
48
50
|
if (isUndefined(resultPath)) {
|
|
49
51
|
throw new EvaluationJsonSchema$anchorError(`Evaluation failed on token: "${token}"`);
|
|
50
52
|
}
|
|
51
|
-
return
|
|
53
|
+
return {
|
|
54
|
+
element: resultPath.node,
|
|
55
|
+
ancestorSchema$ids: schema$idsOf(resultPath.getAncestorNodes().reverse())
|
|
56
|
+
};
|
|
52
57
|
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Evaluates JSON Schema $anchor against ApiDOM fragment.
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
63
|
+
export const evaluate = (anchor, element) => locate(anchor, element).element;
|
|
53
64
|
export { EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError };
|
|
54
65
|
export { default as JsonSchema$anchorError } from "../../../../errors/JsonSchema$anchorError.mjs";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.locate = void 0;
|
|
7
|
+
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
8
|
+
var _util = require("../util.cjs");
|
|
9
|
+
/**
|
|
10
|
+
* Locates the element addressed by JSON Pointer within ApiDOM fragment, along
|
|
11
|
+
* with the `$id`s of the schemas enclosing it: the elements the pointer walks
|
|
12
|
+
* through on its way to the target, the search root included.
|
|
13
|
+
*/
|
|
14
|
+
const locate = (element, jsonPointer) => {
|
|
15
|
+
const trace = {
|
|
16
|
+
steps: []
|
|
17
|
+
};
|
|
18
|
+
const result = (0, _apidomJsonPointer.evaluate)(element, jsonPointer, {
|
|
19
|
+
trace
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// each step's input is the node the token was applied to, so the inputs are
|
|
23
|
+
// exactly the walked nodes minus the target
|
|
24
|
+
return {
|
|
25
|
+
element: result,
|
|
26
|
+
ancestorSchema$ids: (0, _util.schema$idsOf)(trace.steps.map(step => step.input))
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
exports.locate = locate;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { evaluate as jsonPointerEvaluate } from '@speclynx/apidom-json-pointer';
|
|
2
|
+
import { schema$idsOf } from "../util.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Locates the element addressed by JSON Pointer within ApiDOM fragment, along
|
|
5
|
+
* with the `$id`s of the schemas enclosing it: the elements the pointer walks
|
|
6
|
+
* through on its way to the target, the search root included.
|
|
7
|
+
*/
|
|
8
|
+
export const locate = (element, jsonPointer) => {
|
|
9
|
+
const trace = {
|
|
10
|
+
steps: []
|
|
11
|
+
};
|
|
12
|
+
const result = jsonPointerEvaluate(element, jsonPointer, {
|
|
13
|
+
trace
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// each step's input is the node the token was applied to, so the inputs are
|
|
17
|
+
// exactly the walked nodes minus the target
|
|
18
|
+
return {
|
|
19
|
+
element: result,
|
|
20
|
+
ancestorSchema$ids: schema$idsOf(trace.steps.map(step => step.input))
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -17,46 +17,73 @@ Object.defineProperty(exports, "JsonSchemaUriError", {
|
|
|
17
17
|
return _JsonSchemaUriError.default;
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
|
-
exports.evaluate = void 0;
|
|
20
|
+
exports.locate = exports.evaluate = void 0;
|
|
21
21
|
var _ramdaAdjunct = require("ramda-adjunct");
|
|
22
22
|
var _apidomTraverse = require("@speclynx/apidom-traverse");
|
|
23
|
-
var _apidomNsJsonSchema = require("@speclynx/apidom-ns-json-schema-2020-12");
|
|
24
23
|
var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
|
|
25
24
|
var url = _interopRequireWildcard(require("../../../../util/url.cjs"));
|
|
26
25
|
var _EvaluationJsonSchemaUriError = _interopRequireDefault(require("../../../../errors/EvaluationJsonSchemaUriError.cjs"));
|
|
27
|
-
var _$anchor = require("./$anchor.cjs");
|
|
28
26
|
var _util = require("../util.cjs");
|
|
27
|
+
var _$anchor = require("./$anchor.cjs");
|
|
28
|
+
var _jsonPointer = require("./json-pointer.cjs");
|
|
29
29
|
var _JsonSchemaUriError = _interopRequireDefault(require("../../../../errors/JsonSchemaUriError.cjs"));
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
31
|
+
* Index of `$id`-bearing schemas per indexed element (a document root, or the
|
|
32
|
+
* fragment a visitor traverses). Each entry pairs a schema with the `$id`s of its
|
|
33
|
+
* enclosing schema resources (outermost first, including its own). The index is
|
|
34
|
+
* populated once per element and never refreshed, so callers scope it
|
|
35
|
+
* to a single dereference/bundle run instead of sharing it globally. Within a run the
|
|
36
|
+
* `$id` graph of an indexed document is stable: the bundle strategies mutate deep
|
|
37
|
+
* clones and place components only after the entry traversal ends, and the
|
|
38
|
+
* dereference strategies only replace `$ref`-bearing schemas.
|
|
32
39
|
* @public
|
|
33
40
|
*/
|
|
34
|
-
const evaluate = (uri, element) => {
|
|
35
|
-
const {
|
|
36
|
-
cache
|
|
37
|
-
} = evaluate;
|
|
38
|
-
const uriStrippedHash = url.stripHash(uri);
|
|
39
|
-
const isJSONSchemaElementWith$id = path => (0, _apidomNsJsonSchema.isJSONSchemaElement)(path.node) && typeof path.node.$id !== 'undefined';
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* @public
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
const indexSchema$ids = element => (0, _apidomTraverse.filter)(element, path => (0, _util.isJSONSchemaElementWith$id)(path.node)).map(path => ({
|
|
47
|
+
schema: path.node,
|
|
48
|
+
$ids: (0, _util.collectSchema$ids)(path)
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Locates the schema addressed by JSON Schema $ref containing unknown URI within
|
|
53
|
+
* ApiDOM fragment, along with the `$id`s of the schemas enclosing it.
|
|
54
|
+
* @public
|
|
55
|
+
*/
|
|
56
|
+
const locate = (uri, element, options = {}) => {
|
|
57
|
+
const uriStrippedHash = url.stripHash(uri);
|
|
58
|
+
const {
|
|
59
|
+
baseURI = uriStrippedHash,
|
|
60
|
+
index = new WeakMap()
|
|
61
|
+
} = options;
|
|
62
|
+
if (!index.has(element)) {
|
|
63
|
+
index.set(element, indexSchema$ids(element));
|
|
45
64
|
}
|
|
46
65
|
|
|
47
|
-
// search for the
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if ((0, _ramdaAdjunct.isUndefined)(result)) {
|
|
66
|
+
// search for the schema whose canonical URI matches
|
|
67
|
+
const entry = index.get(element).find(({
|
|
68
|
+
$ids
|
|
69
|
+
}) => (0, _util.resolveSchema$ids)(baseURI, $ids) === uriStrippedHash);
|
|
70
|
+
if ((0, _ramdaAdjunct.isUndefined)(entry)) {
|
|
53
71
|
throw new _EvaluationJsonSchemaUriError.default(`Evaluation failed on URI: "${uri}"`);
|
|
54
72
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return
|
|
73
|
+
|
|
74
|
+
// the fragment is resolved within the matched schema, whose own $id is the
|
|
75
|
+
// last of its chain; the fragment walk contributes it again when descending
|
|
76
|
+
const fragmentLocation = (0, _$anchor.isAnchor)((0, _$anchor.uriToAnchor)(uri)) ? (0, _$anchor.locate)((0, _$anchor.uriToAnchor)(uri), entry.schema) : (0, _jsonPointer.locate)(entry.schema, _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(uri));
|
|
77
|
+
return {
|
|
78
|
+
element: fragmentLocation.element,
|
|
79
|
+
ancestorSchema$ids: [...entry.$ids.slice(0, -1), ...fragmentLocation.ancestorSchema$ids]
|
|
80
|
+
};
|
|
60
81
|
};
|
|
61
|
-
|
|
62
|
-
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Evaluates JSON Schema $ref containing unknown URI against ApiDOM fragment.
|
|
85
|
+
* @public
|
|
86
|
+
*/
|
|
87
|
+
exports.locate = locate;
|
|
88
|
+
const evaluate = (uri, element, options = {}) => locate(uri, element, options).element;
|
|
89
|
+
exports.evaluate = evaluate;
|