@speclynx/apidom-reference 5.2.4 → 5.2.6
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 +19 -0
- package/README.md +37 -14
- package/dist/apidom-reference.browser.js +1234 -596
- package/dist/apidom-reference.browser.min.js +1 -1
- package/package.json +29 -29
- 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 +20 -17
- package/src/dereference/strategies/arazzo-1/index.mjs +14 -11
- 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/source-descriptions.cjs +65 -42
- package/src/dereference/strategies/arazzo-1/source-descriptions.mjs +64 -42
- 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 +97 -35
- package/src/dereference/strategies/arazzo-1/visitor.mjs +101 -39
- 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
|
@@ -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();
|
|
@@ -223,34 +243,38 @@ class Arazzo1DereferenceVisitor {
|
|
|
223
243
|
return;
|
|
224
244
|
}
|
|
225
245
|
const runtimeExpression = toValue(referencingElement.reference);
|
|
226
|
-
|
|
227
|
-
// parse the runtime expression
|
|
228
|
-
const {
|
|
229
|
-
result,
|
|
230
|
-
tree
|
|
231
|
-
} = parseRuntimeExpression(runtimeExpression);
|
|
232
|
-
if (!result.success) {
|
|
233
|
-
throw new ApiDOMStructuredError(`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
|
|
234
|
-
runtimeExpression
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// ReusableElement can only reference components
|
|
239
|
-
if (tree.type !== 'ComponentsExpression') {
|
|
240
|
-
throw new ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
|
|
241
|
-
runtimeExpression
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// evaluate runtime expression as JSON Pointer to get the referenced element
|
|
246
|
-
const jsonPointer = jsonPointerCompile(['components', tree.componentType, tree.componentName]);
|
|
247
246
|
let referencedElement;
|
|
248
247
|
try {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
});
|
|
248
|
+
// parse the runtime expression
|
|
249
|
+
const {
|
|
250
|
+
result,
|
|
251
|
+
tree
|
|
252
|
+
} = parseRuntimeExpression(runtimeExpression);
|
|
253
|
+
if (!result.success) {
|
|
254
|
+
throw new ApiDOMStructuredError(`Invalid Reusable Object reference format: "${runtimeExpression}"`, {
|
|
255
|
+
runtimeExpression
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ReusableElement can only reference components
|
|
260
|
+
if (tree.type !== 'ComponentsExpression') {
|
|
261
|
+
throw new ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" must be a components expression`, {
|
|
262
|
+
runtimeExpression
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// evaluate runtime expression as JSON Pointer to get the referenced element
|
|
267
|
+
const jsonPointer = jsonPointerCompile(['components', tree.componentType, tree.componentName]);
|
|
268
|
+
try {
|
|
269
|
+
referencedElement = jsonPointerEvaluate(this.reference.value.result, jsonPointer);
|
|
270
|
+
} catch {
|
|
271
|
+
throw new ApiDOMStructuredError(`Reusable Object reference "${runtimeExpression}" cannot be resolved`, {
|
|
272
|
+
runtimeExpression
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
this.handleError(`Error while dereferencing Reusable Object. Cannot resolve reference "${runtimeExpression}": ${error.message}`, error, referencingElement, 'reference', runtimeExpression, path);
|
|
277
|
+
return;
|
|
254
278
|
}
|
|
255
279
|
|
|
256
280
|
/**
|
|
@@ -298,7 +322,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
298
322
|
let {
|
|
299
323
|
uri: retrievalURI
|
|
300
324
|
} = reference;
|
|
301
|
-
|
|
325
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
326
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
327
|
+
const rootBaseURI = resolveSchema$ids(this.baseURI, this.ancestorSchema$ids);
|
|
328
|
+
const schemaBaseURI = resolveSchemaBaseURI(rootBaseURI, path);
|
|
329
|
+
const $refBaseURI = resolveSchema$refField(schemaBaseURI, referencingElement);
|
|
302
330
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
303
331
|
const file = new File({
|
|
304
332
|
uri: $refBaseURIStrippedHash
|
|
@@ -310,13 +338,32 @@ class Arazzo1DereferenceVisitor {
|
|
|
310
338
|
|
|
311
339
|
// determining reference, proper evaluation and selection mechanism
|
|
312
340
|
let referencedElement;
|
|
341
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
342
|
+
let ancestorSchema$ids;
|
|
313
343
|
try {
|
|
314
344
|
if (isUnknownURI || isURL) {
|
|
315
345
|
// we're dealing with canonical URI or URL with possible fragment
|
|
316
346
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
317
347
|
const selector = $refBaseURI;
|
|
318
|
-
const
|
|
319
|
-
|
|
348
|
+
const documentElement = reference.value.result;
|
|
349
|
+
const referenceAsSchema = maybeRefractToJSONSchemaElement(documentElement);
|
|
350
|
+
// a document that is itself a fragment detached from its source carries
|
|
351
|
+
// the $ids enclosing it only as `ancestorSchema$ids`: its $ids are then
|
|
352
|
+
// indexed against the base that chain leaves in effect, and the chain
|
|
353
|
+
// prefixes the one of the located schema. A nested visitor rooted at a
|
|
354
|
+
// fragment of a whole document indexes that document against its own base.
|
|
355
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
356
|
+
const isDocumentRoot = (isParseResultElement(rootNode) ? rootNode.result : rootNode) === documentElement;
|
|
357
|
+
({
|
|
358
|
+
element: referencedElement,
|
|
359
|
+
ancestorSchema$ids
|
|
360
|
+
} = uriLocate(selector, referenceAsSchema, {
|
|
361
|
+
baseURI: isDocumentRoot ? rootBaseURI : this.baseURI,
|
|
362
|
+
index: this.schema$idIndex
|
|
363
|
+
}));
|
|
364
|
+
if (isDocumentRoot) {
|
|
365
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
366
|
+
}
|
|
320
367
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
321
368
|
|
|
322
369
|
// ignore resolving internal Schema Objects
|
|
@@ -348,7 +395,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
348
395
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
349
396
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
350
397
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
351
|
-
|
|
398
|
+
({
|
|
399
|
+
element: referencedElement,
|
|
400
|
+
ancestorSchema$ids
|
|
401
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
352
402
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
353
403
|
}
|
|
354
404
|
} catch (error) {
|
|
@@ -375,7 +425,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
375
425
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
376
426
|
const selector = uriToAnchor($refBaseURI);
|
|
377
427
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
378
|
-
|
|
428
|
+
({
|
|
429
|
+
element: referencedElement,
|
|
430
|
+
ancestorSchema$ids
|
|
431
|
+
} = $anchorLocate(selector, referenceAsSchema));
|
|
379
432
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
380
433
|
} else {
|
|
381
434
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -396,7 +449,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
396
449
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
397
450
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
398
451
|
const referenceAsSchema = maybeRefractToJSONSchemaElement(reference.value.result);
|
|
399
|
-
|
|
452
|
+
({
|
|
453
|
+
element: referencedElement,
|
|
454
|
+
ancestorSchema$ids
|
|
455
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
400
456
|
referencedElement = maybeRefractToJSONSchemaElement(referencedElement);
|
|
401
457
|
}
|
|
402
458
|
} else {
|
|
@@ -453,7 +509,11 @@ class Arazzo1DereferenceVisitor {
|
|
|
453
509
|
*/
|
|
454
510
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
455
511
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
456
|
-
|
|
512
|
+
// content transplanted into the referencing site is traversed under that
|
|
513
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
514
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
515
|
+
const hasDifferentEnclosingBase = resolveSchema$ids(resolveArazzo$selfField(reference.uri, reference.value.result), ancestorSchema$ids) !== resolveSchema$ids(rootBaseURI, schema$idsOf(path.getAncestorNodes().reverse()));
|
|
516
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || isJSONSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
457
517
|
if (shouldDetectCircular) {
|
|
458
518
|
this.visited.add(referencedElement);
|
|
459
519
|
}
|
|
@@ -464,8 +524,10 @@ class Arazzo1DereferenceVisitor {
|
|
|
464
524
|
reference,
|
|
465
525
|
indirections: [...this.indirections],
|
|
466
526
|
options: this.options,
|
|
527
|
+
schema$idIndex: this.schema$idIndex,
|
|
467
528
|
visited: this.visited,
|
|
468
|
-
ancestors: ancestorsLineage
|
|
529
|
+
ancestors: ancestorsLineage,
|
|
530
|
+
ancestorSchema$ids
|
|
469
531
|
});
|
|
470
532
|
referencedElement = await traverseAsync(referencedElement, visitor, {
|
|
471
533
|
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;
|
|
@@ -1,42 +1,66 @@
|
|
|
1
1
|
import { isUndefined } from 'ramda-adjunct';
|
|
2
2
|
import { filter } from '@speclynx/apidom-traverse';
|
|
3
|
-
import {
|
|
4
|
-
import { URIFragmentIdentifier, evaluate as jsonPointerEvaluate } from '@speclynx/apidom-json-pointer';
|
|
3
|
+
import { URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
|
|
5
4
|
import * as url from "../../../../util/url.mjs";
|
|
6
5
|
import EvaluationJsonSchemaUriError from "../../../../errors/EvaluationJsonSchemaUriError.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { isJSONSchemaElementWith$id, collectSchema$ids, resolveSchema$ids } from "../util.mjs";
|
|
7
|
+
import { isAnchor, uriToAnchor, locate as $anchorLocate } from "./$anchor.mjs";
|
|
8
|
+
import { locate as jsonPointerLocate } from "./json-pointer.mjs";
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
10
|
+
* Index of `$id`-bearing schemas per indexed element (a document root, or the
|
|
11
|
+
* fragment a visitor traverses). Each entry pairs a schema with the `$id`s of its
|
|
12
|
+
* enclosing schema resources (outermost first, including its own). The index is
|
|
13
|
+
* populated once per element and never refreshed, so callers scope it
|
|
14
|
+
* to a single dereference/bundle run instead of sharing it globally. Within a run the
|
|
15
|
+
* `$id` graph of an indexed document is stable: the bundle strategies mutate deep
|
|
16
|
+
* clones and place components only after the entry traversal ends, and the
|
|
17
|
+
* dereference strategies only replace `$ref`-bearing schemas.
|
|
11
18
|
* @public
|
|
12
19
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
const indexSchema$ids = element => filter(element, path => isJSONSchemaElementWith$id(path.node)).map(path => ({
|
|
24
|
+
schema: path.node,
|
|
25
|
+
$ids: collectSchema$ids(path)
|
|
26
|
+
}));
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Locates the schema addressed by JSON Schema $ref containing unknown URI within
|
|
30
|
+
* ApiDOM fragment, along with the `$id`s of the schemas enclosing it.
|
|
31
|
+
* @public
|
|
32
|
+
*/
|
|
33
|
+
export const locate = (uri, element, options = {}) => {
|
|
34
|
+
const uriStrippedHash = url.stripHash(uri);
|
|
35
|
+
const {
|
|
36
|
+
baseURI = uriStrippedHash,
|
|
37
|
+
index = new WeakMap()
|
|
38
|
+
} = options;
|
|
39
|
+
if (!index.has(element)) {
|
|
40
|
+
index.set(element, indexSchema$ids(element));
|
|
24
41
|
}
|
|
25
42
|
|
|
26
|
-
// search for the
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (isUndefined(result)) {
|
|
43
|
+
// search for the schema whose canonical URI matches
|
|
44
|
+
const entry = index.get(element).find(({
|
|
45
|
+
$ids
|
|
46
|
+
}) => resolveSchema$ids(baseURI, $ids) === uriStrippedHash);
|
|
47
|
+
if (isUndefined(entry)) {
|
|
32
48
|
throw new EvaluationJsonSchemaUriError(`Evaluation failed on URI: "${uri}"`);
|
|
33
49
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return
|
|
50
|
+
|
|
51
|
+
// the fragment is resolved within the matched schema, whose own $id is the
|
|
52
|
+
// last of its chain; the fragment walk contributes it again when descending
|
|
53
|
+
const fragmentLocation = isAnchor(uriToAnchor(uri)) ? $anchorLocate(uriToAnchor(uri), entry.schema) : jsonPointerLocate(entry.schema, URIFragmentIdentifier.fromURIReference(uri));
|
|
54
|
+
return {
|
|
55
|
+
element: fragmentLocation.element,
|
|
56
|
+
ancestorSchema$ids: [...entry.$ids.slice(0, -1), ...fragmentLocation.ancestorSchema$ids]
|
|
57
|
+
};
|
|
39
58
|
};
|
|
40
|
-
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Evaluates JSON Schema $ref containing unknown URI against ApiDOM fragment.
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export const evaluate = (uri, element, options = {}) => locate(uri, element, options).element;
|
|
41
65
|
export { EvaluationJsonSchemaUriError };
|
|
42
66
|
export { default as JsonSchemaUriError } from "../../../../errors/JsonSchemaUriError.mjs";
|