@speclynx/apidom-reference 5.2.2 → 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 +20 -0
- package/README.md +30 -11
- package/dist/apidom-reference.browser.js +1121 -516
- 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
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { propEq, none } from 'ramda';
|
|
2
2
|
import { isUndefined } from 'ramda-adjunct';
|
|
3
|
-
import { isElement, isStringElement, isObjectElement, RefElement, cloneShallow, cloneDeep } from '@speclynx/apidom-datamodel';
|
|
3
|
+
import { isElement, isStringElement, isObjectElement, isParseResultElement, RefElement, cloneShallow, cloneDeep } from '@speclynx/apidom-datamodel';
|
|
4
4
|
import { toValue, fixedFields, toYAML } from '@speclynx/apidom-core';
|
|
5
5
|
import { ApiDOMStructuredError } from '@speclynx/apidom-error';
|
|
6
6
|
import { traverse, traverseAsync, find } from '@speclynx/apidom-traverse';
|
|
7
7
|
import { evaluate as jsonPointerEvaluate, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
|
|
8
8
|
import { isReferenceLikeElement, isPathItemElement, isReferenceElement, isSchemaElement, isOperationElement, isBooleanJSONSchemaElement, refract, refractReference, refractPathItem, refractOperation } from '@speclynx/apidom-ns-openapi-3-1';
|
|
9
|
-
import { isAnchor, uriToAnchor,
|
|
10
|
-
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 "./selectors/json-pointer.mjs";
|
|
11
12
|
import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
|
|
12
13
|
import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
|
|
13
14
|
import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
|
|
@@ -16,7 +17,7 @@ import * as url from "../../../util/url.mjs";
|
|
|
16
17
|
import parse from "../../../parse/index.mjs";
|
|
17
18
|
import Reference from "../../../Reference.mjs";
|
|
18
19
|
import File from "../../../File.mjs";
|
|
19
|
-
import { resolveSchema$refField, maybeRefractToSchemaElement } from "./util.mjs";
|
|
20
|
+
import { schema$idsOf, resolveSchema$ids, resolveSchemaBaseURI, resolveSchema$refField, maybeRefractToSchemaElement } from "./util.mjs";
|
|
20
21
|
import { AncestorLineage } from "../../util.mjs";
|
|
21
22
|
/**
|
|
22
23
|
* @public
|
|
@@ -29,8 +30,23 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
29
30
|
reference;
|
|
30
31
|
options;
|
|
31
32
|
refractCache;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Index of `$id`-bearing schemas per document, shared across nested visitors
|
|
36
|
+
* of a single dereference run so it never outlives mutations of the documents.
|
|
37
|
+
*/
|
|
38
|
+
schema$idIndex;
|
|
32
39
|
visited;
|
|
33
40
|
|
|
41
|
+
/**
|
|
42
|
+
* `$id`s of the JSON Schema elements enclosing the traversal root within its
|
|
43
|
+
* document (outermost first). Empty when the root is the document itself; a
|
|
44
|
+
* nested visitor traversing a fragment is seeded with the `$id`s of the
|
|
45
|
+
* schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
|
|
46
|
+
* against the same base as they would from the document root.
|
|
47
|
+
*/
|
|
48
|
+
ancestorSchema$ids;
|
|
49
|
+
|
|
34
50
|
/**
|
|
35
51
|
* Tracks element ancestors across dive-deep traversal boundaries.
|
|
36
52
|
* Used for cycle detection: if a referenced element is found in
|
|
@@ -42,15 +58,19 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
42
58
|
options,
|
|
43
59
|
indirections = [],
|
|
44
60
|
refractCache = new WeakMap(),
|
|
61
|
+
schema$idIndex = new WeakMap(),
|
|
45
62
|
ancestors = new AncestorLineage(),
|
|
46
|
-
visited = new WeakSet()
|
|
63
|
+
visited = new WeakSet(),
|
|
64
|
+
ancestorSchema$ids = []
|
|
47
65
|
}) {
|
|
48
66
|
this.indirections = indirections;
|
|
49
67
|
this.reference = reference;
|
|
50
68
|
this.options = options;
|
|
51
69
|
this.refractCache = refractCache;
|
|
70
|
+
this.schema$idIndex = schema$idIndex;
|
|
52
71
|
this.ancestors = new AncestorLineage(...ancestors);
|
|
53
72
|
this.visited = visited;
|
|
73
|
+
this.ancestorSchema$ids = ancestorSchema$ids;
|
|
54
74
|
}
|
|
55
75
|
toAncestorLineage(path) {
|
|
56
76
|
const ancestorNodes = path.getAncestorNodes();
|
|
@@ -309,6 +329,7 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
309
329
|
indirections: [...this.indirections],
|
|
310
330
|
options: this.options,
|
|
311
331
|
refractCache: this.refractCache,
|
|
332
|
+
schema$idIndex: this.schema$idIndex,
|
|
312
333
|
visited: this.visited,
|
|
313
334
|
ancestors: ancestorsLineage
|
|
314
335
|
});
|
|
@@ -467,6 +488,7 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
467
488
|
indirections: [...this.indirections],
|
|
468
489
|
options: this.options,
|
|
469
490
|
refractCache: this.refractCache,
|
|
491
|
+
schema$idIndex: this.schema$idIndex,
|
|
470
492
|
visited: this.visited,
|
|
471
493
|
ancestors: ancestorsLineage
|
|
472
494
|
});
|
|
@@ -664,7 +686,11 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
664
686
|
let {
|
|
665
687
|
uri: retrievalURI
|
|
666
688
|
} = reference;
|
|
667
|
-
|
|
689
|
+
// base URI in effect at the traversal root: the document base refined by
|
|
690
|
+
// the $ids of the schemas enclosing the root when the root is a fragment
|
|
691
|
+
const rootBaseURI = resolveSchema$ids(retrievalURI, this.ancestorSchema$ids);
|
|
692
|
+
const schemaBaseURI = resolveSchemaBaseURI(rootBaseURI, path);
|
|
693
|
+
const $refBaseURI = resolveSchema$refField(schemaBaseURI, referencingElement);
|
|
668
694
|
const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
|
|
669
695
|
const file = new File({
|
|
670
696
|
uri: $refBaseURIStrippedHash
|
|
@@ -676,13 +702,48 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
676
702
|
|
|
677
703
|
// determining reference, proper evaluation and selection mechanism
|
|
678
704
|
let referencedElement;
|
|
705
|
+
// $ids of the schemas enclosing the referenced element within its document
|
|
706
|
+
let ancestorSchema$ids;
|
|
679
707
|
try {
|
|
680
708
|
if (isUnknownURI || isURL) {
|
|
681
709
|
// we're dealing with canonical URI or URL with possible fragment
|
|
682
710
|
retrievalURI = this.toBaseURI($refBaseURI);
|
|
683
711
|
const selector = $refBaseURI;
|
|
684
|
-
|
|
685
|
-
|
|
712
|
+
// the fragment being traversed is searched before the whole document: a
|
|
713
|
+
// fragment referenced from another document (a Response, Parameter, Path
|
|
714
|
+
// Item, ...) is refracted with its own semantics, so the Schema Objects it
|
|
715
|
+
// contains are indexed, whereas the referenced document as a whole is
|
|
716
|
+
// parsed generically and refracted as a bare JSON Schema, which leaves
|
|
717
|
+
// those Schema Objects (under `content`, `schema`, ...) unrecognized and
|
|
718
|
+
// their `$id`s out of the index
|
|
719
|
+
const documentElement = reference.value.result;
|
|
720
|
+
const rootNode = path.getAncestorNodes().at(-1) ?? path.node;
|
|
721
|
+
const fragmentElement = isParseResultElement(rootNode) ? rootNode.result : rootNode;
|
|
722
|
+
try {
|
|
723
|
+
// the fragment's $ids resolve against the base its enclosing chain
|
|
724
|
+
// leaves in effect, which then also prefixes the located schema's chain
|
|
725
|
+
({
|
|
726
|
+
element: referencedElement,
|
|
727
|
+
ancestorSchema$ids
|
|
728
|
+
} = uriLocate(selector, maybeRefractToSchemaElement(fragmentElement), {
|
|
729
|
+
baseURI: rootBaseURI,
|
|
730
|
+
index: this.schema$idIndex
|
|
731
|
+
}));
|
|
732
|
+
ancestorSchema$ids = [...this.ancestorSchema$ids, ...ancestorSchema$ids];
|
|
733
|
+
} catch (error) {
|
|
734
|
+
if (!(error instanceof EvaluationJsonSchemaUriError) || fragmentElement === documentElement) {
|
|
735
|
+
throw error;
|
|
736
|
+
}
|
|
737
|
+
// the document index is rooted at the document, so the located chain
|
|
738
|
+
// is already document-relative and takes no prefix
|
|
739
|
+
({
|
|
740
|
+
element: referencedElement,
|
|
741
|
+
ancestorSchema$ids
|
|
742
|
+
} = uriLocate(selector, maybeRefractToSchemaElement(documentElement), {
|
|
743
|
+
baseURI: reference.uri,
|
|
744
|
+
index: this.schema$idIndex
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
686
747
|
referencedElement = maybeRefractToSchemaElement(referencedElement);
|
|
687
748
|
|
|
688
749
|
// ignore resolving internal Schema Objects
|
|
@@ -714,7 +775,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
714
775
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
715
776
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
716
777
|
const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
|
|
717
|
-
|
|
778
|
+
({
|
|
779
|
+
element: referencedElement,
|
|
780
|
+
ancestorSchema$ids
|
|
781
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
718
782
|
referencedElement = maybeRefractToSchemaElement(referencedElement);
|
|
719
783
|
}
|
|
720
784
|
} catch (error) {
|
|
@@ -741,7 +805,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
741
805
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
742
806
|
const selector = uriToAnchor($refBaseURI);
|
|
743
807
|
const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
|
|
744
|
-
|
|
808
|
+
({
|
|
809
|
+
element: referencedElement,
|
|
810
|
+
ancestorSchema$ids
|
|
811
|
+
} = $anchorLocate(selector, referenceAsSchema));
|
|
745
812
|
referencedElement = maybeRefractToSchemaElement(referencedElement);
|
|
746
813
|
} else {
|
|
747
814
|
// we're assuming here that we're dealing with JSON Pointer here
|
|
@@ -762,7 +829,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
762
829
|
reference = await this.toReference(url.unsanitize($refBaseURI));
|
|
763
830
|
const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
|
|
764
831
|
const referenceAsSchema = maybeRefractToSchemaElement(reference.value.result);
|
|
765
|
-
|
|
832
|
+
({
|
|
833
|
+
element: referencedElement,
|
|
834
|
+
ancestorSchema$ids
|
|
835
|
+
} = jsonPointerLocate(referenceAsSchema, selector));
|
|
766
836
|
referencedElement = maybeRefractToSchemaElement(referencedElement);
|
|
767
837
|
}
|
|
768
838
|
} else {
|
|
@@ -819,7 +889,11 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
819
889
|
*/
|
|
820
890
|
const isNonEntryDocument = url.stripHash(reference.refSet.rootRef.uri) !== reference.uri;
|
|
821
891
|
const shouldDetectCircular = ['error', 'replace'].includes(this.options.dereference.circular);
|
|
822
|
-
|
|
892
|
+
// content transplanted into the referencing site is traversed under that
|
|
893
|
+
// site's $id chain; a fragment enclosed by a different chain has its own
|
|
894
|
+
// $refs resolved in place first, by a nested visitor seeded with that chain
|
|
895
|
+
const hasDifferentEnclosingBase = resolveSchema$ids(reference.uri, ancestorSchema$ids) !== resolveSchema$ids(rootBaseURI, schema$idsOf(path.getAncestorNodes().reverse()));
|
|
896
|
+
if ((isExternalReference || isNonEntryDocument || hasDifferentEnclosingBase || isSchemaElement(referencedElement) && isStringElement(referencedElement.$ref) || shouldDetectCircular && !this.visited.has(referencedElement)) && !ancestorsLineage.includesCycle(referencedElement)) {
|
|
823
897
|
if (shouldDetectCircular) {
|
|
824
898
|
this.visited.add(referencedElement);
|
|
825
899
|
}
|
|
@@ -829,8 +903,10 @@ class OpenAPI3_1DereferenceVisitor {
|
|
|
829
903
|
indirections: [...this.indirections],
|
|
830
904
|
options: this.options,
|
|
831
905
|
refractCache: this.refractCache,
|
|
906
|
+
schema$idIndex: this.schema$idIndex,
|
|
832
907
|
visited: this.visited,
|
|
833
|
-
ancestors: ancestorsLineage
|
|
908
|
+
ancestors: ancestorsLineage,
|
|
909
|
+
ancestorSchema$ids
|
|
834
910
|
});
|
|
835
911
|
referencedElement = await traverseAsync(referencedElement, visitor, {
|
|
836
912
|
mutable: true,
|
package/src/util/url.cjs
CHANGED
|
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequ
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
|
|
7
|
+
exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.relative = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
|
|
8
8
|
var _process = _interopRequireDefault(require("process"));
|
|
9
9
|
var _ramda = require("ramda");
|
|
10
10
|
var _ramdaAdjunct = require("ramda-adjunct");
|
|
@@ -275,6 +275,39 @@ const resolve = (from, to) => {
|
|
|
275
275
|
return resolvedUrl.toString();
|
|
276
276
|
};
|
|
277
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Computes the URI reference that resolves to `to` against `from` — the inverse
|
|
280
|
+
* of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
|
|
281
|
+
* relative to the directory of `from` (its last path segment is the document,
|
|
282
|
+
* not a directory). When the two URIs differ in scheme or authority, or either
|
|
283
|
+
* has no hierarchical path (e.g. a URN), no relative form exists and `to` is
|
|
284
|
+
* returned unchanged.
|
|
285
|
+
* @public
|
|
286
|
+
*/
|
|
287
|
+
exports.resolve = resolve;
|
|
288
|
+
const relative = (from, to) => {
|
|
289
|
+
const fromURL = new URL(from, 'resolve://');
|
|
290
|
+
const toURL = new URL(to, 'resolve://');
|
|
291
|
+
|
|
292
|
+
// the authority is host plus userinfo; a relative reference keeps both from `from`
|
|
293
|
+
if (fromURL.protocol !== toURL.protocol || fromURL.host !== toURL.host || fromURL.username !== toURL.username || fromURL.password !== toURL.password || !fromURL.pathname.startsWith('/') || !toURL.pathname.startsWith('/')) {
|
|
294
|
+
return to;
|
|
295
|
+
}
|
|
296
|
+
const fromSegments = fromURL.pathname.split('/').slice(0, -1);
|
|
297
|
+
const toSegments = toURL.pathname.split('/');
|
|
298
|
+
let common = 0;
|
|
299
|
+
while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
|
|
300
|
+
common += 1;
|
|
301
|
+
}
|
|
302
|
+
const segments = [...fromSegments.slice(common).map(() => '..'), ...toSegments.slice(common)];
|
|
303
|
+
// a leading segment with a colon would be parsed as a scheme, and an empty
|
|
304
|
+
// reference (`to` is the directory of `from`) would resolve back to `from`
|
|
305
|
+
if (segments[0].includes(':') || segments.join('/') === '') {
|
|
306
|
+
segments.unshift('.');
|
|
307
|
+
}
|
|
308
|
+
return segments.join('/') + toURL.search + toURL.hash;
|
|
309
|
+
};
|
|
310
|
+
|
|
278
311
|
/**
|
|
279
312
|
* Sanitizes/Encodes URI to it's url encoded form.
|
|
280
313
|
*
|
|
@@ -283,7 +316,7 @@ const resolve = (from, to) => {
|
|
|
283
316
|
* by first unsatizing it and then performing sanitization again.
|
|
284
317
|
* @public
|
|
285
318
|
*/
|
|
286
|
-
exports.
|
|
319
|
+
exports.relative = relative;
|
|
287
320
|
const sanitize = uri => {
|
|
288
321
|
if (isFileSystemPath(uri)) {
|
|
289
322
|
return fromFileSystemPath(toFileSystemPath(uri));
|
package/src/util/url.mjs
CHANGED
|
@@ -259,6 +259,38 @@ export const resolve = (from, to) => {
|
|
|
259
259
|
return resolvedUrl.toString();
|
|
260
260
|
};
|
|
261
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Computes the URI reference that resolves to `to` against `from` — the inverse
|
|
264
|
+
* of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
|
|
265
|
+
* relative to the directory of `from` (its last path segment is the document,
|
|
266
|
+
* not a directory). When the two URIs differ in scheme or authority, or either
|
|
267
|
+
* has no hierarchical path (e.g. a URN), no relative form exists and `to` is
|
|
268
|
+
* returned unchanged.
|
|
269
|
+
* @public
|
|
270
|
+
*/
|
|
271
|
+
export const relative = (from, to) => {
|
|
272
|
+
const fromURL = new URL(from, 'resolve://');
|
|
273
|
+
const toURL = new URL(to, 'resolve://');
|
|
274
|
+
|
|
275
|
+
// the authority is host plus userinfo; a relative reference keeps both from `from`
|
|
276
|
+
if (fromURL.protocol !== toURL.protocol || fromURL.host !== toURL.host || fromURL.username !== toURL.username || fromURL.password !== toURL.password || !fromURL.pathname.startsWith('/') || !toURL.pathname.startsWith('/')) {
|
|
277
|
+
return to;
|
|
278
|
+
}
|
|
279
|
+
const fromSegments = fromURL.pathname.split('/').slice(0, -1);
|
|
280
|
+
const toSegments = toURL.pathname.split('/');
|
|
281
|
+
let common = 0;
|
|
282
|
+
while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
|
|
283
|
+
common += 1;
|
|
284
|
+
}
|
|
285
|
+
const segments = [...fromSegments.slice(common).map(() => '..'), ...toSegments.slice(common)];
|
|
286
|
+
// a leading segment with a colon would be parsed as a scheme, and an empty
|
|
287
|
+
// reference (`to` is the directory of `from`) would resolve back to `from`
|
|
288
|
+
if (segments[0].includes(':') || segments.join('/') === '') {
|
|
289
|
+
segments.unshift('.');
|
|
290
|
+
}
|
|
291
|
+
return segments.join('/') + toURL.search + toURL.hash;
|
|
292
|
+
};
|
|
293
|
+
|
|
262
294
|
/**
|
|
263
295
|
* Sanitizes/Encodes URI to it's url encoded form.
|
|
264
296
|
*
|
|
@@ -491,6 +491,17 @@ export declare interface ReferenceSetOptions {
|
|
|
491
491
|
readonly circular?: boolean;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Computes the URI reference that resolves to `to` against `from` — the inverse
|
|
496
|
+
* of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
|
|
497
|
+
* relative to the directory of `from` (its last path segment is the document,
|
|
498
|
+
* not a directory). When the two URIs differ in scheme or authority, or either
|
|
499
|
+
* has no hierarchical path (e.g. a URN), no relative form exists and `to` is
|
|
500
|
+
* returned unchanged.
|
|
501
|
+
* @public
|
|
502
|
+
*/
|
|
503
|
+
declare const relative: (from: string, to: string) => string;
|
|
504
|
+
|
|
494
505
|
/**
|
|
495
506
|
* @public
|
|
496
507
|
*/
|
|
@@ -647,6 +658,7 @@ declare namespace url {
|
|
|
647
658
|
stripHash,
|
|
648
659
|
cwd,
|
|
649
660
|
resolve_2 as resolve,
|
|
661
|
+
relative,
|
|
650
662
|
sanitize,
|
|
651
663
|
unsanitize
|
|
652
664
|
}
|
|
@@ -7,7 +7,7 @@ export type { default as DereferenceStrategy, DereferenceStrategyOptions, } from
|
|
|
7
7
|
export type { default as File, FileOptions } from '../../../File.ts';
|
|
8
8
|
export type { default as Reference, ReferenceOptions } from '../../../Reference.ts';
|
|
9
9
|
export type { default as ReferenceSet, ReferenceSetOptions } from '../../../ReferenceSet.ts';
|
|
10
|
-
export type { Arazzo1BundleVisitorOptions } from './visitor.ts';
|
|
10
|
+
export type { Arazzo1BundleVisitorOptions, InputPlacement } from './visitor.ts';
|
|
11
11
|
export type { ReferenceOptions as ApiDOMReferenceOptions, ReferenceBundleOptions as ApiDOMReferenceBundleOptions, ReferenceDereferenceOptions as ApiDOMReferenceDereferenceOptions, ReferenceParseOptions as ApiDOMReferenceParseOptions, ReferenceResolveOptions as ApiDOMReferenceResolveOptions, } from '../../../options/index.ts';
|
|
12
12
|
export type { default as Parser, ParserOptions } from '../../../parse/parsers/Parser.ts';
|
|
13
13
|
export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
|
|
@@ -2,7 +2,17 @@ import { Element, ObjectElement, ParseResultElement } from '@speclynx/apidom-dat
|
|
|
2
2
|
import { type Path } from '@speclynx/apidom-traverse';
|
|
3
3
|
import { ArazzoSpecification1Element } from '@speclynx/apidom-ns-arazzo-1';
|
|
4
4
|
import Reference from '../../../Reference.ts';
|
|
5
|
+
import { type Schema$idIndex } from '../../../dereference/strategies/arazzo-1/selectors/uri.ts';
|
|
5
6
|
import type { ReferenceOptions } from '../../../options/index.ts';
|
|
7
|
+
/**
|
|
8
|
+
* A bundled JSON Schema resource awaiting placement into the entry document's
|
|
9
|
+
* `components.inputs`.
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface InputPlacement {
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly element: Element;
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* @public
|
|
8
18
|
*/
|
|
@@ -11,6 +21,8 @@ export interface Arazzo1BundleVisitorOptions {
|
|
|
11
21
|
readonly options: ReferenceOptions;
|
|
12
22
|
readonly assignments?: Map<string, string>;
|
|
13
23
|
readonly reservedNames?: Map<string, Set<string>>;
|
|
24
|
+
readonly placements?: InputPlacement[];
|
|
25
|
+
readonly schema$idIndex?: Schema$idIndex;
|
|
14
26
|
}
|
|
15
27
|
/**
|
|
16
28
|
* @public
|
|
@@ -46,7 +58,28 @@ declare class Arazzo1BundleVisitor {
|
|
|
46
58
|
* components that are still being bundled.
|
|
47
59
|
*/
|
|
48
60
|
protected readonly reservedNames: Map<string, Set<string>>;
|
|
49
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Embedded resources waiting to be placed into the entry document's
|
|
63
|
+
* `components.inputs`, shared across the entry document and every external
|
|
64
|
+
* document visitor. Placement is deferred until the entry document traversal
|
|
65
|
+
* completes: the traversal walks live content, so placing a resource mid-way
|
|
66
|
+
* would make it descend into the resource and re-bundle its `$ref`s against
|
|
67
|
+
* the entry document's base URI instead of the resource's own.
|
|
68
|
+
*/
|
|
69
|
+
protected readonly placements: InputPlacement[];
|
|
70
|
+
/**
|
|
71
|
+
* Index of `$id`-bearing schemas per document, shared across the entry document
|
|
72
|
+
* and every external document visitor of a single bundle run so it never
|
|
73
|
+
* outlives mutations of the documents (e.g. the final placement of inputs).
|
|
74
|
+
*/
|
|
75
|
+
protected readonly schema$idIndex: Schema$idIndex;
|
|
76
|
+
constructor({ reference, options, assignments, reservedNames, placements, schema$idIndex, }: Arazzo1BundleVisitorOptions);
|
|
77
|
+
/**
|
|
78
|
+
* Base URI of the entry document — its `$self` when present, otherwise its
|
|
79
|
+
* retrieval URI. It is the base against which a relative `$id` written into
|
|
80
|
+
* `components.inputs` resolves.
|
|
81
|
+
*/
|
|
82
|
+
protected get entryBaseURI(): string;
|
|
50
83
|
protected toBaseURI(uri: string): string;
|
|
51
84
|
protected toReference(uri: string): Promise<Reference>;
|
|
52
85
|
/**
|
|
@@ -72,8 +105,8 @@ declare class Arazzo1BundleVisitor {
|
|
|
72
105
|
protected baseName(element: Element, baseURI: string): string;
|
|
73
106
|
/**
|
|
74
107
|
* Computes a collision-free component name within `components.inputs`. A name
|
|
75
|
-
* is taken if it's already
|
|
76
|
-
* still being bundled (reserved before recursion).
|
|
108
|
+
* is taken if it's already present in the entry document's inputs or reserved
|
|
109
|
+
* by a component that is still being bundled (reserved before recursion).
|
|
77
110
|
*/
|
|
78
111
|
protected uniqueName(candidate: string): string;
|
|
79
112
|
/**
|
|
@@ -82,14 +115,26 @@ declare class Arazzo1BundleVisitor {
|
|
|
82
115
|
* its own component (and origin), so report the rename per severity.
|
|
83
116
|
*/
|
|
84
117
|
protected reportNameCollision(preferredName: string, componentName: string): void;
|
|
118
|
+
/**
|
|
119
|
+
* The entry document traversal is rooted at its Parse Result (nested
|
|
120
|
+
* traversals are rooted at schema resources), so leaving it marks the moment
|
|
121
|
+
* the whole entry document has been visited and the collected resources can
|
|
122
|
+
* be placed into `components.inputs` without the traversal descending into them.
|
|
123
|
+
* Guarded on the node so that a traversal rooted at any other Parse Result
|
|
124
|
+
* never flushes the shared placements early.
|
|
125
|
+
*/
|
|
126
|
+
readonly ParseResultElement: {
|
|
127
|
+
leave: (path: Path<Element>) => void;
|
|
128
|
+
};
|
|
85
129
|
/**
|
|
86
130
|
* JSON Schema Objects in Arazzo (used by Input Objects and inline schemas) are
|
|
87
131
|
* a JSON Schema 2020-12 dialect — the only external reference type Arazzo
|
|
88
132
|
* defines. They are bundled per the JSON Schema Compound Document rules: the
|
|
89
133
|
* external schema RESOURCE is embedded verbatim into `components.inputs`
|
|
90
|
-
* (carrying its `$id`), and the referencing `$ref` is
|
|
91
|
-
*
|
|
92
|
-
*
|
|
134
|
+
* (carrying its `$id`), and the referencing `$ref` is rebased onto that `$id`
|
|
135
|
+
* when it resolves to a different URI (a location-based `$ref` to a
|
|
136
|
+
* self-identifying resource); otherwise it is left unchanged. This mirrors
|
|
137
|
+
* the openapi-3-1 strategy's Schema Object handling.
|
|
93
138
|
*/
|
|
94
139
|
JSONSchemaElement(path: Path<Element>): Promise<void>;
|
|
95
140
|
}
|
|
@@ -7,7 +7,7 @@ export type { default as DereferenceStrategy, DereferenceStrategyOptions, } from
|
|
|
7
7
|
export type { default as File, FileOptions } from '../../../File.ts';
|
|
8
8
|
export type { default as Reference, ReferenceOptions } from '../../../Reference.ts';
|
|
9
9
|
export type { default as ReferenceSet, ReferenceSetOptions } from '../../../ReferenceSet.ts';
|
|
10
|
-
export type { OpenAPI3_1BundleVisitorOptions } from './visitor.ts';
|
|
10
|
+
export type { OpenAPI3_1BundleVisitorOptions, ComponentPlacement } from './visitor.ts';
|
|
11
11
|
export type { ReferenceOptions as ApiDOMReferenceOptions, ReferenceBundleOptions as ApiDOMReferenceBundleOptions, ReferenceDereferenceOptions as ApiDOMReferenceDereferenceOptions, ReferenceParseOptions as ApiDOMReferenceParseOptions, ReferenceResolveOptions as ApiDOMReferenceResolveOptions, } from '../../../options/index.ts';
|
|
12
12
|
export type { default as Parser, ParserOptions } from '../../../parse/parsers/Parser.ts';
|
|
13
13
|
export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
|
|
@@ -2,7 +2,18 @@ import { Element, ObjectElement, ParseResultElement } from '@speclynx/apidom-dat
|
|
|
2
2
|
import { type Path } from '@speclynx/apidom-traverse';
|
|
3
3
|
import { OpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
|
|
4
4
|
import Reference from '../../../Reference.ts';
|
|
5
|
+
import { type Schema$idIndex } from '../../../dereference/strategies/openapi-3-1/selectors/uri.ts';
|
|
5
6
|
import type { ReferenceOptions } from '../../../options/index.ts';
|
|
7
|
+
/**
|
|
8
|
+
* A bundled element awaiting placement into the entry document's Components
|
|
9
|
+
* Object field.
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export interface ComponentPlacement {
|
|
13
|
+
readonly field: string;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly element: Element;
|
|
16
|
+
}
|
|
6
17
|
/**
|
|
7
18
|
* @public
|
|
8
19
|
*/
|
|
@@ -12,6 +23,9 @@ export interface OpenAPI3_1BundleVisitorOptions {
|
|
|
12
23
|
readonly assignments?: Map<string, string>;
|
|
13
24
|
readonly reservedNames?: Map<string, Set<string>>;
|
|
14
25
|
readonly refractCache?: WeakMap<Element, Map<string, Element>>;
|
|
26
|
+
readonly schema$idIndex?: Schema$idIndex;
|
|
27
|
+
readonly placements?: ComponentPlacement[];
|
|
28
|
+
readonly relocationBaseURI?: string;
|
|
15
29
|
}
|
|
16
30
|
/**
|
|
17
31
|
* @public
|
|
@@ -54,7 +68,30 @@ declare class OpenAPI3_1BundleVisitor {
|
|
|
54
68
|
* generic object referenced once as a Parameter and once as a Response).
|
|
55
69
|
*/
|
|
56
70
|
protected readonly refractCache: WeakMap<Element, Map<string, Element>>;
|
|
57
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Index of `$id`-bearing schemas per document, shared across the entry document
|
|
73
|
+
* and every external document visitor of a single bundle run so it never
|
|
74
|
+
* outlives mutations of the documents (e.g. the final placement of components).
|
|
75
|
+
*/
|
|
76
|
+
protected readonly schema$idIndex: Schema$idIndex;
|
|
77
|
+
/**
|
|
78
|
+
* Bundled fragments waiting to be placed into the entry document's
|
|
79
|
+
* Components Object, shared across the entry document and every external
|
|
80
|
+
* document visitor. Placement is deferred until the entry document traversal
|
|
81
|
+
* completes: the traversal walks live content, so placing a fragment mid-way
|
|
82
|
+
* would make it descend into the fragment and re-bundle its `$ref`s against
|
|
83
|
+
* the entry document's base URI instead of the fragment's own.
|
|
84
|
+
*/
|
|
85
|
+
protected readonly placements: ComponentPlacement[];
|
|
86
|
+
/**
|
|
87
|
+
* The base URI the traversed content will have once placed into the entry
|
|
88
|
+
* document, when placement changes it: content hoisted out of a non-schema
|
|
89
|
+
* document (a Response, Parameter, Path Item, ...) trades that document's
|
|
90
|
+
* base URI for the entry document's. Unset for the entry document itself and
|
|
91
|
+
* for embedded schema resources, whose `$id` carries their base along.
|
|
92
|
+
*/
|
|
93
|
+
protected readonly relocationBaseURI: string | undefined;
|
|
94
|
+
constructor({ reference, options, assignments, reservedNames, refractCache, schema$idIndex, placements, relocationBaseURI, }: OpenAPI3_1BundleVisitorOptions);
|
|
58
95
|
protected getRefracted(source: Element, type: string): Element | undefined;
|
|
59
96
|
protected setRefracted(source: Element, type: string, refracted: Element): void;
|
|
60
97
|
protected toBaseURI(uri: string): string;
|
|
@@ -65,6 +102,14 @@ declare class OpenAPI3_1BundleVisitor {
|
|
|
65
102
|
* transferable. Bare fragments are returned unchanged.
|
|
66
103
|
*/
|
|
67
104
|
protected normalizeSelfReference(ref: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* The base URI the given Schema Object's `$ref` will be resolved against once
|
|
107
|
+
* the content is placed into the entry document, or `undefined` when the
|
|
108
|
+
* `$ref` as written resolves to `$refBaseURI` from there as well: the content
|
|
109
|
+
* isn't relocated, an absolute `$id` on the schema's ancestor chain pins its
|
|
110
|
+
* base, or the hoisted document sits next to the entry document.
|
|
111
|
+
*/
|
|
112
|
+
protected relocatedSchemaBaseURI(path: Path<Element>, $refBaseURI: string): string | undefined;
|
|
68
113
|
/**
|
|
69
114
|
* Handles an error according to the `bundle.continueOnError` option.
|
|
70
115
|
*
|
|
@@ -93,8 +138,8 @@ declare class OpenAPI3_1BundleVisitor {
|
|
|
93
138
|
protected baseName(element: Element, field: string, jsonPointer: string, baseURI: string): string;
|
|
94
139
|
/**
|
|
95
140
|
* Computes a collision-free component name within the target field. A name is
|
|
96
|
-
* taken if it's already
|
|
97
|
-
* is still being bundled (reserved before recursion).
|
|
141
|
+
* taken if it's already present in the entry document's components or reserved
|
|
142
|
+
* by a component that is still being bundled (reserved before recursion).
|
|
98
143
|
*/
|
|
99
144
|
protected uniqueName(candidate: string, field: string): string;
|
|
100
145
|
/**
|
|
@@ -103,6 +148,17 @@ declare class OpenAPI3_1BundleVisitor {
|
|
|
103
148
|
* its own component (and origin), so report the rename per severity.
|
|
104
149
|
*/
|
|
105
150
|
protected reportNameCollision(preferredName: string, componentName: string, field: string): void;
|
|
151
|
+
/**
|
|
152
|
+
* The entry document traversal is rooted at its Parse Result (nested
|
|
153
|
+
* traversals are rooted at fragments), so leaving it marks the moment the
|
|
154
|
+
* whole entry document has been visited and the collected fragments can be
|
|
155
|
+
* placed into its Components Object without the traversal descending into them.
|
|
156
|
+
* Guarded on the node so that a traversal rooted at any other Parse Result
|
|
157
|
+
* never flushes the shared placements early.
|
|
158
|
+
*/
|
|
159
|
+
readonly ParseResultElement: {
|
|
160
|
+
leave: (path: Path<Element>) => void;
|
|
161
|
+
};
|
|
106
162
|
ReferenceElement(path: Path<Element>): Promise<void>;
|
|
107
163
|
/**
|
|
108
164
|
* Unlike OpenAPI 3.0 (which has no `components.pathItems` field and therefore
|
package/types/bundle/util.d.ts
CHANGED
|
@@ -20,3 +20,42 @@ export declare const sanitizeComponentName: (value: string) => string;
|
|
|
20
20
|
* @public
|
|
21
21
|
*/
|
|
22
22
|
export declare const uniqueName: (candidate: string, isTaken: (name: string) => boolean) => string;
|
|
23
|
+
/**
|
|
24
|
+
* Computes the `$ref` value addressing an embedded JSON Schema resource by its
|
|
25
|
+
* canonical `$id`. `$refURI` is the `$ref` resolved against the referencing
|
|
26
|
+
* document's base URI; `resourceURI` is the embedded resource's canonical URI
|
|
27
|
+
* (its `$id`, or the retrieval URI when it has none).
|
|
28
|
+
*
|
|
29
|
+
* When the two differ — a location-based `$ref` (e.g. `./ex.json#/$defs/Pet`)
|
|
30
|
+
* to a resource that identifies itself differently (`$id: https://example.com/pets`)
|
|
31
|
+
* — the `$ref` is rebased onto the resource URI, keeping its `$anchor` or JSON
|
|
32
|
+
* Pointer fragment. Once embedded, the resource is reachable only by its `$id`,
|
|
33
|
+
* so an unchanged `$ref` would dangle. When they already agree, `undefined` is
|
|
34
|
+
* returned and the `$ref` is to be left as written.
|
|
35
|
+
*
|
|
36
|
+
* This knowingly departs from the letter of JSON Schema 2020-12 §9.3.1
|
|
37
|
+
* ("references ... MUST NOT be changed"), which presumes `$ref`s are written
|
|
38
|
+
* against `$id`s. The target schema — and thus evaluation output — is unchanged;
|
|
39
|
+
* only the alias the loader knew the resource by is normalized to its canonical URI.
|
|
40
|
+
*
|
|
41
|
+
* @public
|
|
42
|
+
*/
|
|
43
|
+
export declare const rebaseSchema$ref: ($refURI: string, resourceURI: string) => string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Computes the `$ref` value addressing an embedded JSON Schema resource from a
|
|
46
|
+
* Schema Object whose base URI changes when it is relocated into the entry
|
|
47
|
+
* document — one carried inside a hoisted Response, Parameter, Path Item, etc.
|
|
48
|
+
* `$refURI` and `resourceURI` are as in `rebaseSchema$ref`; `baseURI` is the
|
|
49
|
+
* base the `$ref` resolves against after relocation.
|
|
50
|
+
*
|
|
51
|
+
* A resource that identifies itself (`isResourceIdentified`, i.e. it declares a
|
|
52
|
+
* `$id`) is addressed by that `$id`, exactly as `rebaseSchema$ref` does. A
|
|
53
|
+
* resource identified only by its retrieval URI is addressed by a URI reference
|
|
54
|
+
* relative to `baseURI`: it then resolves to the `$id` assigned from that
|
|
55
|
+
* retrieval URI wherever the bundle is placed, and no absolute retrieval
|
|
56
|
+
* location leaks into the `$ref`. The `$anchor` or JSON Pointer fragment is
|
|
57
|
+
* kept in both cases.
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
export declare const relocateSchema$ref: ($refURI: string, resourceURI: string, baseURI: string, isResourceIdentified: boolean) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { isAnchor, uriToAnchor, parse, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError, } from '../../openapi-3-1/selectors/$anchor.ts';
|
|
1
|
+
export { isAnchor, uriToAnchor, parse, locate, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError, } from '../../openapi-3-1/selectors/$anchor.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError, } from '../../openapi-3-1/selectors/uri.ts';
|
|
1
|
+
export { locate, evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError, type EvaluateOptions, type Schema$idIndex, } from '../../openapi-3-1/selectors/uri.ts';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Element, ParseResultElement } from '@speclynx/apidom-datamodel';
|
|
2
2
|
import { SourceDescriptionElement } from '@speclynx/apidom-ns-arazzo-1';
|
|
3
3
|
import type Reference from '../../../Reference.ts';
|
|
4
|
-
export { resolveSchema$refField, resolveSchema$idField } from '../openapi-3-1/util.ts';
|
|
4
|
+
export { resolveSchema$refField, resolveSchema$idField, resolveSchemaBaseURI, } from '../openapi-3-1/util.ts';
|
|
5
5
|
/**
|
|
6
6
|
* Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
|
|
7
7
|
*
|