@speclynx/apidom-reference 5.1.1 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-reference",
3
- "version": "5.1.1",
3
+ "version": "5.2.1",
4
4
  "description": "Advanced algorithms for semantic ApiDOM manipulations like dereferencing or resolution.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -299,35 +299,35 @@
299
299
  "license": "Apache-2.0",
300
300
  "dependencies": {
301
301
  "@babel/runtime-corejs3": "^8.0.0",
302
- "@speclynx/apidom-core": "5.1.1",
303
- "@speclynx/apidom-datamodel": "5.1.1",
304
- "@speclynx/apidom-error": "5.1.1",
305
- "@speclynx/apidom-json-pointer": "5.1.1",
306
- "@speclynx/apidom-ns-arazzo-1": "5.1.1",
307
- "@speclynx/apidom-ns-asyncapi-2": "5.1.1",
308
- "@speclynx/apidom-ns-json-schema-2020-12": "5.1.1",
309
- "@speclynx/apidom-ns-openapi-2": "5.1.1",
310
- "@speclynx/apidom-ns-openapi-3-0": "5.1.1",
311
- "@speclynx/apidom-ns-openapi-3-1": "5.1.1",
312
- "@speclynx/apidom-ns-overlay-1": "5.1.1",
313
- "@speclynx/apidom-parser-adapter-arazzo-json-1": "5.1.1",
314
- "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "5.1.1",
315
- "@speclynx/apidom-parser-adapter-asyncapi-json-2": "5.1.1",
316
- "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "5.1.1",
317
- "@speclynx/apidom-parser-adapter-json": "5.1.1",
318
- "@speclynx/apidom-parser-adapter-openapi-json-2": "5.1.1",
319
- "@speclynx/apidom-parser-adapter-openapi-json-3-0": "5.1.1",
320
- "@speclynx/apidom-parser-adapter-openapi-json-3-1": "5.1.1",
321
- "@speclynx/apidom-parser-adapter-openapi-yaml-2": "5.1.1",
322
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "5.1.1",
323
- "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "5.1.1",
324
- "@speclynx/apidom-parser-adapter-overlay-json-1": "5.1.1",
325
- "@speclynx/apidom-parser-adapter-overlay-yaml-1": "5.1.1",
326
- "@speclynx/apidom-parser-adapter-yaml-1-2": "5.1.1",
327
- "@speclynx/apidom-traverse": "5.1.1",
302
+ "@speclynx/apidom-core": "5.2.1",
303
+ "@speclynx/apidom-datamodel": "5.2.1",
304
+ "@speclynx/apidom-error": "5.2.1",
305
+ "@speclynx/apidom-json-pointer": "5.2.1",
306
+ "@speclynx/apidom-ns-arazzo-1": "5.2.1",
307
+ "@speclynx/apidom-ns-asyncapi-2": "5.2.1",
308
+ "@speclynx/apidom-ns-json-schema-2020-12": "5.2.1",
309
+ "@speclynx/apidom-ns-openapi-2": "5.2.1",
310
+ "@speclynx/apidom-ns-openapi-3-0": "5.2.1",
311
+ "@speclynx/apidom-ns-openapi-3-1": "5.2.1",
312
+ "@speclynx/apidom-ns-overlay-1": "5.2.1",
313
+ "@speclynx/apidom-parser-adapter-arazzo-json-1": "5.2.1",
314
+ "@speclynx/apidom-parser-adapter-arazzo-yaml-1": "5.2.1",
315
+ "@speclynx/apidom-parser-adapter-asyncapi-json-2": "5.2.1",
316
+ "@speclynx/apidom-parser-adapter-asyncapi-yaml-2": "5.2.1",
317
+ "@speclynx/apidom-parser-adapter-json": "5.2.1",
318
+ "@speclynx/apidom-parser-adapter-openapi-json-2": "5.2.1",
319
+ "@speclynx/apidom-parser-adapter-openapi-json-3-0": "5.2.1",
320
+ "@speclynx/apidom-parser-adapter-openapi-json-3-1": "5.2.1",
321
+ "@speclynx/apidom-parser-adapter-openapi-yaml-2": "5.2.1",
322
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-0": "5.2.1",
323
+ "@speclynx/apidom-parser-adapter-openapi-yaml-3-1": "5.2.1",
324
+ "@speclynx/apidom-parser-adapter-overlay-json-1": "5.2.1",
325
+ "@speclynx/apidom-parser-adapter-overlay-yaml-1": "5.2.1",
326
+ "@speclynx/apidom-parser-adapter-yaml-1-2": "5.2.1",
327
+ "@speclynx/apidom-traverse": "5.2.1",
328
328
  "@swaggerexpert/arazzo-runtime-expression": "^3.1.0",
329
329
  "axios": "^1.19.0",
330
- "picomatch": "^4.0.5",
330
+ "picomatch": "^4.0.7",
331
331
  "process": "^0.11.10",
332
332
  "ramda": "~0.32.0",
333
333
  "ramda-adjunct": "^6.1.0"
@@ -346,5 +346,5 @@
346
346
  "README.md",
347
347
  "CHANGELOG.md"
348
348
  ],
349
- "gitHead": "b661f6f038953d118ad09eec0d7da7927db46f5c"
349
+ "gitHead": "814e67e36b1e27546288c74a25eab8f9463943eb"
350
350
  }
@@ -43,6 +43,13 @@ const cf = (0, _apidomCore.fixedFields)(_apidomNsArazzo.ComponentsElement, {
43
43
  */
44
44
  class Arazzo1BundleVisitor {
45
45
  reference;
46
+
47
+ /**
48
+ * Base URI of the current document for resolving relative references.
49
+ * Honors the Arazzo `$self` field when present, otherwise falls back
50
+ * to the retrieval URI (`this.reference.uri`).
51
+ */
52
+ baseURI;
46
53
  options;
47
54
 
48
55
  /**
@@ -81,12 +88,13 @@ class Arazzo1BundleVisitor {
81
88
  reservedNames = new Map()
82
89
  }) {
83
90
  this.reference = reference;
91
+ this.baseURI = (0, _util.resolveArazzo$selfField)(reference.uri, reference.value?.result);
84
92
  this.options = options;
85
93
  this.assignments = assignments;
86
94
  this.reservedNames = reservedNames;
87
95
  }
88
96
  toBaseURI(uri) {
89
- return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
97
+ return url.resolve(this.baseURI, url.sanitize(url.stripHash(uri)));
90
98
  }
91
99
  async toReference(uri) {
92
100
  // detect maximum depth of resolution
@@ -105,6 +113,12 @@ class Arazzo1BundleVisitor {
105
113
  if (refSet.has(baseURI)) {
106
114
  return refSet.find(ref => ref.uri === baseURI);
107
115
  }
116
+
117
+ // identity-based referencing: URI matches `$self` of an already processed Arazzo document
118
+ const referenceBy$self = refSet.find((0, _util.identifiedBy$self)(baseURI));
119
+ if (referenceBy$self !== undefined) {
120
+ return referenceBy$self;
121
+ }
108
122
  const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
109
123
  ...this.options,
110
124
  parse: {
@@ -279,19 +293,18 @@ class Arazzo1BundleVisitor {
279
293
  }
280
294
  const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
281
295
  try {
282
- // compute baseURI using rules around $id and $ref keywords. The current
283
- // document's URI is the retrieval URI — derive it directly (not via
284
- // toReference) so an internal $ref needs no resolution and never trips the
296
+ // compute baseURI using rules around $self, $id and $ref keywords. The
297
+ // current document's base URI is derived directly (not via toReference)
298
+ // so an internal $ref needs no resolution and never trips the
285
299
  // resolve.maxDepth guard.
286
- const retrievalURI = this.reference.uri;
287
- const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, referencingElement);
300
+ const $refBaseURI = (0, _util.resolveSchema$refField)(this.baseURI, referencingElement);
288
301
  const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
289
302
  const file = new _File.default({
290
303
  uri: $refBaseURIStrippedHash
291
304
  });
292
305
  const isUnknownURI = (0, _ramda.none)(r => r.canRead(file), this.options.resolve.resolvers);
293
306
  const isURL = !isUnknownURI;
294
- const isInternalReference = url.stripHash(this.reference.uri) === $refBaseURIStrippedHash;
307
+ const isInternalReference = this.baseURI === $refBaseURIStrippedHash;
295
308
  const isExternalReference = !isInternalReference;
296
309
 
297
310
  // internal Schema Object $refs resolve against the in-document $id graph;
@@ -345,7 +358,10 @@ class Arazzo1BundleVisitor {
345
358
  // is internal even when its URI form (e.g. a URN or absolute $id) is not a
346
359
  // bare fragment. Leave it untouched — it resolves against the in-document
347
360
  // $id graph, and embedding the current document into itself would be wrong.
348
- if (url.stripHash(schemaReference.uri) === url.stripHash(this.reference.uri)) {
361
+ // The same holds for a $ref resolving into the entry document (e.g. by its
362
+ // `$self` identity) — it's already part of the bundle.
363
+ const schemaReferenceURI = url.stripHash(schemaReference.uri);
364
+ if (schemaReferenceURI === url.stripHash(this.reference.uri) || schemaReferenceURI === url.stripHash(this.reference.refSet.rootRef.uri)) {
349
365
  return;
350
366
  }
351
367
 
@@ -15,7 +15,7 @@ import Reference from "../../../Reference.mjs";
15
15
  import File from "../../../File.mjs";
16
16
  import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "../../../dereference/strategies/arazzo-1/selectors/$anchor.mjs";
17
17
  import { evaluate as uriEvaluate } from "../../../dereference/strategies/arazzo-1/selectors/uri.mjs";
18
- import { resolveSchema$refField, resolveSchema$idField, maybeRefractToJSONSchemaElement } from "../../../dereference/strategies/arazzo-1/util.mjs";
18
+ import { resolveSchema$refField, resolveSchema$idField, maybeRefractToJSONSchemaElement, resolveArazzo$selfField, identifiedBy$self } from "../../../dereference/strategies/arazzo-1/util.mjs";
19
19
  import { toPascalCase, sanitizeComponentName, uniqueName as resolveUniqueName } from "../../util.mjs";
20
20
  /**
21
21
  * The Components Object field where embedded JSON Schema resources are hoisted.
@@ -35,6 +35,13 @@ const cf = fixedFields(ComponentsElement, {
35
35
  */
36
36
  class Arazzo1BundleVisitor {
37
37
  reference;
38
+
39
+ /**
40
+ * Base URI of the current document for resolving relative references.
41
+ * Honors the Arazzo `$self` field when present, otherwise falls back
42
+ * to the retrieval URI (`this.reference.uri`).
43
+ */
44
+ baseURI;
38
45
  options;
39
46
 
40
47
  /**
@@ -73,12 +80,13 @@ class Arazzo1BundleVisitor {
73
80
  reservedNames = new Map()
74
81
  }) {
75
82
  this.reference = reference;
83
+ this.baseURI = resolveArazzo$selfField(reference.uri, reference.value?.result);
76
84
  this.options = options;
77
85
  this.assignments = assignments;
78
86
  this.reservedNames = reservedNames;
79
87
  }
80
88
  toBaseURI(uri) {
81
- return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
89
+ return url.resolve(this.baseURI, url.sanitize(url.stripHash(uri)));
82
90
  }
83
91
  async toReference(uri) {
84
92
  // detect maximum depth of resolution
@@ -97,6 +105,12 @@ class Arazzo1BundleVisitor {
97
105
  if (refSet.has(baseURI)) {
98
106
  return refSet.find(ref => ref.uri === baseURI);
99
107
  }
108
+
109
+ // identity-based referencing: URI matches `$self` of an already processed Arazzo document
110
+ const referenceBy$self = refSet.find(identifiedBy$self(baseURI));
111
+ if (referenceBy$self !== undefined) {
112
+ return referenceBy$self;
113
+ }
100
114
  const parseResult = await parse(url.unsanitize(baseURI), {
101
115
  ...this.options,
102
116
  parse: {
@@ -271,19 +285,18 @@ class Arazzo1BundleVisitor {
271
285
  }
272
286
  const $ref = toValue(referencingElement.$ref);
273
287
  try {
274
- // compute baseURI using rules around $id and $ref keywords. The current
275
- // document's URI is the retrieval URI — derive it directly (not via
276
- // toReference) so an internal $ref needs no resolution and never trips the
288
+ // compute baseURI using rules around $self, $id and $ref keywords. The
289
+ // current document's base URI is derived directly (not via toReference)
290
+ // so an internal $ref needs no resolution and never trips the
277
291
  // resolve.maxDepth guard.
278
- const retrievalURI = this.reference.uri;
279
- const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
292
+ const $refBaseURI = resolveSchema$refField(this.baseURI, referencingElement);
280
293
  const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
281
294
  const file = new File({
282
295
  uri: $refBaseURIStrippedHash
283
296
  });
284
297
  const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
285
298
  const isURL = !isUnknownURI;
286
- const isInternalReference = url.stripHash(this.reference.uri) === $refBaseURIStrippedHash;
299
+ const isInternalReference = this.baseURI === $refBaseURIStrippedHash;
287
300
  const isExternalReference = !isInternalReference;
288
301
 
289
302
  // internal Schema Object $refs resolve against the in-document $id graph;
@@ -337,7 +350,10 @@ class Arazzo1BundleVisitor {
337
350
  // is internal even when its URI form (e.g. a URN or absolute $id) is not a
338
351
  // bare fragment. Leave it untouched — it resolves against the in-document
339
352
  // $id graph, and embedding the current document into itself would be wrong.
340
- if (url.stripHash(schemaReference.uri) === url.stripHash(this.reference.uri)) {
353
+ // The same holds for a $ref resolving into the entry document (e.g. by its
354
+ // `$self` identity) — it's already part of the bundle.
355
+ const schemaReferenceURI = url.stripHash(schemaReference.uri);
356
+ if (schemaReferenceURI === url.stripHash(this.reference.uri) || schemaReferenceURI === url.stripHash(this.reference.refSet.rootRef.uri)) {
341
357
  return;
342
358
  }
343
359
 
@@ -17,12 +17,24 @@ Object.defineProperty(exports, "dereferenceSourceDescriptions", {
17
17
  return _sourceDescriptions.dereferenceSourceDescriptions;
18
18
  }
19
19
  });
20
+ Object.defineProperty(exports, "identifiedBy$self", {
21
+ enumerable: true,
22
+ get: function () {
23
+ return _util.identifiedBy$self;
24
+ }
25
+ });
20
26
  Object.defineProperty(exports, "maybeRefractToJSONSchemaElement", {
21
27
  enumerable: true,
22
28
  get: function () {
23
29
  return _util.maybeRefractToJSONSchemaElement;
24
30
  }
25
31
  });
32
+ Object.defineProperty(exports, "resolveArazzo$selfField", {
33
+ enumerable: true,
34
+ get: function () {
35
+ return _util.resolveArazzo$selfField;
36
+ }
37
+ });
26
38
  Object.defineProperty(exports, "resolveSchema$idField", {
27
39
  enumerable: true,
28
40
  get: function () {
@@ -104,6 +104,6 @@ class Arazzo1DereferenceStrategy extends DereferenceStrategy {
104
104
  }
105
105
  }
106
106
  export { Arazzo1DereferenceVisitor };
107
- export { resolveSchema$refField, resolveSchema$idField, maybeRefractToJSONSchemaElement } from "./util.mjs";
107
+ export { resolveSchema$refField, resolveSchema$idField, resolveArazzo$selfField, identifiedBy$self, maybeRefractToJSONSchemaElement } from "./util.mjs";
108
108
  export { dereferenceSourceDescriptions } from "./source-descriptions.mjs";
109
109
  export default Arazzo1DereferenceStrategy;
@@ -14,6 +14,7 @@ var _apidomCore = require("@speclynx/apidom-core");
14
14
  var url = _interopRequireWildcard(require("../../../util/url.cjs"));
15
15
  var _util = require("../../../options/util.cjs");
16
16
  var _index = _interopRequireWildcard(require("../../index.cjs"));
17
+ var _util2 = require("./util.cjs");
17
18
  /**
18
19
  * Dereferences a single source description element.
19
20
  * Returns ParseResultElement on success, or with annotation if skipped.
@@ -183,7 +184,7 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
183
184
  * @public
184
185
  */
185
186
  async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalURI, options, strategyName = 'arazzo-1') {
186
- const baseURI = url.sanitize(url.stripHash(parseResultRetrievalURI));
187
+ const retrievalURI = url.sanitize(url.stripHash(parseResultRetrievalURI));
187
188
  const results = [];
188
189
 
189
190
  // get API from dereferenced parse result
@@ -214,10 +215,14 @@ async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalUR
214
215
  const currentDepth = sharedOpts.sourceDescriptionsDepth ?? 0;
215
216
  const visitedUrls = sharedOpts.sourceDescriptionsVisitedUrls ?? new Set();
216
217
 
217
- // add current file to visited URLs to prevent cycles
218
+ // base URI for resolving source description URLs honors Arazzo `$self` field
219
+ const baseURI = (0, _util2.resolveArazzo$selfField)(retrievalURI, api);
220
+
221
+ // add current file to visited URLs to prevent cycles (by retrieval URI and by identity)
222
+ visitedUrls.add(retrievalURI);
218
223
  visitedUrls.add(baseURI);
219
224
  if (currentDepth >= maxDepth) {
220
- const annotation = new _apidomDatamodel.AnnotationElement(`Maximum dereference depth of ${maxDepth} has been exceeded by file "${baseURI}"`);
225
+ const annotation = new _apidomDatamodel.AnnotationElement(`Maximum dereference depth of ${maxDepth} has been exceeded by file "${retrievalURI}"`);
221
226
  annotation.classes.push('error');
222
227
  const parseResult = new _apidomDatamodel.ParseResultElement([annotation]);
223
228
  parseResult.classes.push('source-description');
@@ -7,6 +7,7 @@ import { toValue } from '@speclynx/apidom-core';
7
7
  import * as url from "../../../util/url.mjs";
8
8
  import { merge as mergeOptions } from "../../../options/util.mjs";
9
9
  import dereference, { dereferenceApiDOM } from "../../index.mjs";
10
+ import { resolveArazzo$selfField } from "./util.mjs";
10
11
  /**
11
12
  * Dereferences a single source description element.
12
13
  * Returns ParseResultElement on success, or with annotation if skipped.
@@ -176,7 +177,7 @@ async function dereferenceSourceDescription(sourceDescription, ctx) {
176
177
  * @public
177
178
  */
178
179
  export async function dereferenceSourceDescriptions(parseResult, parseResultRetrievalURI, options, strategyName = 'arazzo-1') {
179
- const baseURI = url.sanitize(url.stripHash(parseResultRetrievalURI));
180
+ const retrievalURI = url.sanitize(url.stripHash(parseResultRetrievalURI));
180
181
  const results = [];
181
182
 
182
183
  // get API from dereferenced parse result
@@ -207,10 +208,14 @@ export async function dereferenceSourceDescriptions(parseResult, parseResultRetr
207
208
  const currentDepth = sharedOpts.sourceDescriptionsDepth ?? 0;
208
209
  const visitedUrls = sharedOpts.sourceDescriptionsVisitedUrls ?? new Set();
209
210
 
210
- // add current file to visited URLs to prevent cycles
211
+ // base URI for resolving source description URLs honors Arazzo `$self` field
212
+ const baseURI = resolveArazzo$selfField(retrievalURI, api);
213
+
214
+ // add current file to visited URLs to prevent cycles (by retrieval URI and by identity)
215
+ visitedUrls.add(retrievalURI);
211
216
  visitedUrls.add(baseURI);
212
217
  if (currentDepth >= maxDepth) {
213
- const annotation = new AnnotationElement(`Maximum dereference depth of ${maxDepth} has been exceeded by file "${baseURI}"`);
218
+ const annotation = new AnnotationElement(`Maximum dereference depth of ${maxDepth} has been exceeded by file "${retrievalURI}"`);
214
219
  annotation.classes.push('error');
215
220
  const parseResult = new ParseResultElement([annotation]);
216
221
  parseResult.classes.push('source-description');
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.refractToJSONSchemaElement = exports.maybeRefractToJSONSchemaElement = void 0;
7
+ exports.resolveArazzo$selfField = exports.refractToJSONSchemaElement = exports.maybeRefractToJSONSchemaElement = exports.identifiedBy$self = void 0;
7
8
  Object.defineProperty(exports, "resolveSchema$idField", {
8
9
  enumerable: true,
9
10
  get: function () {
@@ -17,11 +18,50 @@ Object.defineProperty(exports, "resolveSchema$refField", {
17
18
  }
18
19
  });
19
20
  var _apidomDatamodel = require("@speclynx/apidom-datamodel");
21
+ var _apidomCore = require("@speclynx/apidom-core");
20
22
  var _apidomNsArazzo = require("@speclynx/apidom-ns-arazzo-1");
23
+ var url = _interopRequireWildcard(require("../../../util/url.cjs"));
21
24
  var _util = require("../openapi-3-1/util.cjs");
25
+ /**
26
+ * Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
27
+ *
28
+ * When the Arazzo Object carries a `$self` field, it establishes the document's
29
+ * canonical identity and the base URI for resolving relative references. `$self`
30
+ * may itself be relative, in which case it's resolved against the retrieval URI.
31
+ * Any fragment is stripped, as `$self` MUST NOT contain one. When `$self` is
32
+ * absent (or `element` is not an Arazzo document), the retrieval URI is the base URI.
33
+ *
34
+ * @public
35
+ */
36
+ const resolveArazzo$selfField = (retrievalURI, element) => {
37
+ if (!(0, _apidomNsArazzo.isArazzoSpecification1Element)(element)) {
38
+ return retrievalURI;
39
+ }
40
+ const $self = (0, _apidomCore.toValue)(element.$self);
41
+ if (typeof $self !== 'string' || $self === '') {
42
+ return retrievalURI;
43
+ }
44
+ return url.resolve(retrievalURI, url.sanitize(url.stripHash($self)));
45
+ };
46
+
47
+ /**
48
+ * Predicate matching a Reference by the identity (`$self`) of the Arazzo
49
+ * document it holds, rather than by its retrieval URI. Implements identity-based
50
+ * referencing from Arazzo 1.1.0: a URI matching a loaded document's `$self`
51
+ * refers to that document, regardless of where it was retrieved from.
52
+ *
53
+ * @public
54
+ */
55
+ exports.resolveArazzo$selfField = resolveArazzo$selfField;
56
+ const identifiedBy$self = uri => reference => {
57
+ const result = reference.value?.result;
58
+ return (0, _apidomNsArazzo.isArazzoSpecification1Element)(result) && resolveArazzo$selfField(reference.uri, result) === uri;
59
+ };
60
+
22
61
  /**
23
62
  * Cached version of JSONSchemaElement.refract.
24
63
  */
64
+ exports.identifiedBy$self = identifiedBy$self;
25
65
  const refractToJSONSchemaElement = element => {
26
66
  if (refractToJSONSchemaElement.cache.has(element)) {
27
67
  return refractToJSONSchemaElement.cache.get(element);
@@ -1,6 +1,43 @@
1
1
  import { isPrimitiveElement } from '@speclynx/apidom-datamodel';
2
- import { refractJSONSchema } from '@speclynx/apidom-ns-arazzo-1';
2
+ import { toValue } from '@speclynx/apidom-core';
3
+ import { refractJSONSchema, isArazzoSpecification1Element } from '@speclynx/apidom-ns-arazzo-1';
4
+ import * as url from "../../../util/url.mjs";
3
5
  export { resolveSchema$refField, resolveSchema$idField } from "../openapi-3-1/util.mjs";
6
+ /**
7
+ * Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
8
+ *
9
+ * When the Arazzo Object carries a `$self` field, it establishes the document's
10
+ * canonical identity and the base URI for resolving relative references. `$self`
11
+ * may itself be relative, in which case it's resolved against the retrieval URI.
12
+ * Any fragment is stripped, as `$self` MUST NOT contain one. When `$self` is
13
+ * absent (or `element` is not an Arazzo document), the retrieval URI is the base URI.
14
+ *
15
+ * @public
16
+ */
17
+ export const resolveArazzo$selfField = (retrievalURI, element) => {
18
+ if (!isArazzoSpecification1Element(element)) {
19
+ return retrievalURI;
20
+ }
21
+ const $self = toValue(element.$self);
22
+ if (typeof $self !== 'string' || $self === '') {
23
+ return retrievalURI;
24
+ }
25
+ return url.resolve(retrievalURI, url.sanitize(url.stripHash($self)));
26
+ };
27
+
28
+ /**
29
+ * Predicate matching a Reference by the identity (`$self`) of the Arazzo
30
+ * document it holds, rather than by its retrieval URI. Implements identity-based
31
+ * referencing from Arazzo 1.1.0: a URI matching a loaded document's `$self`
32
+ * refers to that document, regardless of where it was retrieved from.
33
+ *
34
+ * @public
35
+ */
36
+ export const identifiedBy$self = uri => reference => {
37
+ const result = reference.value?.result;
38
+ return isArazzoSpecification1Element(result) && resolveArazzo$selfField(reference.uri, result) === uri;
39
+ };
40
+
4
41
  /**
5
42
  * Cached version of JSONSchemaElement.refract.
6
43
  */
@@ -37,6 +37,13 @@ var _EvaluationJsonSchemaUriError = _interopRequireDefault(require("../../../err
37
37
  class Arazzo1DereferenceVisitor {
38
38
  indirections;
39
39
  reference;
40
+
41
+ /**
42
+ * Base URI of the current document for resolving relative references.
43
+ * Honors the Arazzo `$self` field when present, otherwise falls back
44
+ * to the retrieval URI (`this.reference.uri`).
45
+ */
46
+ baseURI;
40
47
  options;
41
48
  visited;
42
49
 
@@ -55,6 +62,7 @@ class Arazzo1DereferenceVisitor {
55
62
  }) {
56
63
  this.indirections = indirections;
57
64
  this.reference = reference;
65
+ this.baseURI = (0, _util2.resolveArazzo$selfField)(reference.uri, reference.value?.result);
58
66
  this.options = options;
59
67
  this.ancestors = new _util3.AncestorLineage(...ancestors);
60
68
  this.visited = visited;
@@ -66,7 +74,7 @@ class Arazzo1DereferenceVisitor {
66
74
  return [ancestorsLineage, directAncestors];
67
75
  }
68
76
  toBaseURI(uri) {
69
- return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
77
+ return url.resolve(this.baseURI, url.sanitize(url.stripHash(uri)));
70
78
  }
71
79
  async toReference(uri) {
72
80
  // detect maximum depth of resolution
@@ -85,6 +93,12 @@ class Arazzo1DereferenceVisitor {
85
93
  if (refSet.has(baseURI)) {
86
94
  return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
87
95
  }
96
+
97
+ // identity-based referencing: URI matches `$self` of an already processed Arazzo document
98
+ const referenceBy$self = refSet.find((0, _util2.identifiedBy$self)(baseURI));
99
+ if (referenceBy$self !== undefined) {
100
+ return referenceBy$self;
101
+ }
88
102
  const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
89
103
  ...this.options,
90
104
  parse: {
@@ -288,19 +302,19 @@ class Arazzo1DereferenceVisitor {
288
302
  }
289
303
  const indirectionsSize = this.indirections.length;
290
304
  try {
291
- // compute baseURI using rules around $id and $ref keywords
305
+ // compute baseURI using rules around $self, $id and $ref keywords
292
306
  let reference = await this.toReference(url.unsanitize(this.reference.uri));
293
307
  let {
294
308
  uri: retrievalURI
295
309
  } = reference;
296
- const $refBaseURI = (0, _util.resolveSchema$refField)(retrievalURI, referencingElement);
310
+ const $refBaseURI = (0, _util.resolveSchema$refField)(this.baseURI, referencingElement);
297
311
  const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
298
312
  const file = new _File.default({
299
313
  uri: $refBaseURIStrippedHash
300
314
  });
301
315
  const isUnknownURI = (0, _ramda.none)(r => r.canRead(file), this.options.resolve.resolvers);
302
316
  const isURL = !isUnknownURI;
303
- let isInternalReference = url.stripHash(this.reference.uri) === $refBaseURI;
317
+ let isInternalReference = this.baseURI === $refBaseURI;
304
318
  let isExternalReference = !isInternalReference;
305
319
 
306
320
  // determining reference, proper evaluation and selection mechanism
@@ -327,7 +341,7 @@ class Arazzo1DereferenceVisitor {
327
341
  } else {
328
342
  // we're assuming here that we're dealing with JSON Pointer here
329
343
  retrievalURI = this.toBaseURI($refBaseURI);
330
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
344
+ isInternalReference = this.baseURI === retrievalURI;
331
345
  isExternalReference = !isInternalReference;
332
346
 
333
347
  // ignore resolving internal Schema Objects
@@ -354,7 +368,7 @@ class Arazzo1DereferenceVisitor {
354
368
  if (isURL && error instanceof _EvaluationJsonSchemaUriError.default) {
355
369
  if ((0, _$anchor.isAnchor)((0, _$anchor.uriToAnchor)($refBaseURI))) {
356
370
  // we're dealing with JSON Schema $anchor here
357
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
371
+ isInternalReference = this.baseURI === retrievalURI;
358
372
  isExternalReference = !isInternalReference;
359
373
 
360
374
  // ignore resolving internal Schema Objects
@@ -375,7 +389,7 @@ class Arazzo1DereferenceVisitor {
375
389
  } else {
376
390
  // we're assuming here that we're dealing with JSON Pointer here
377
391
  retrievalURI = this.toBaseURI($refBaseURI);
378
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
392
+ isInternalReference = this.baseURI === retrievalURI;
379
393
  isExternalReference = !isInternalReference;
380
394
 
381
395
  // ignore resolving internal Schema Objects
@@ -9,7 +9,7 @@ import { parse as parseRuntimeExpression } from '@swaggerexpert/arazzo-runtime-e
9
9
  import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "./selectors/$anchor.mjs";
10
10
  import { evaluate as uriEvaluate } from "./selectors/uri.mjs";
11
11
  import { resolveSchema$refField } from "../openapi-3-1/util.mjs";
12
- import { maybeRefractToJSONSchemaElement } from "./util.mjs";
12
+ import { maybeRefractToJSONSchemaElement, resolveArazzo$selfField, identifiedBy$self } from "./util.mjs";
13
13
  import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
14
14
  import MaximumDereferenceDepthError from "../../../errors/MaximumDereferenceDepthError.mjs";
15
15
  import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
@@ -28,6 +28,13 @@ import EvaluationJsonSchemaUriError from "../../../errors/EvaluationJsonSchemaUr
28
28
  class Arazzo1DereferenceVisitor {
29
29
  indirections;
30
30
  reference;
31
+
32
+ /**
33
+ * Base URI of the current document for resolving relative references.
34
+ * Honors the Arazzo `$self` field when present, otherwise falls back
35
+ * to the retrieval URI (`this.reference.uri`).
36
+ */
37
+ baseURI;
31
38
  options;
32
39
  visited;
33
40
 
@@ -46,6 +53,7 @@ class Arazzo1DereferenceVisitor {
46
53
  }) {
47
54
  this.indirections = indirections;
48
55
  this.reference = reference;
56
+ this.baseURI = resolveArazzo$selfField(reference.uri, reference.value?.result);
49
57
  this.options = options;
50
58
  this.ancestors = new AncestorLineage(...ancestors);
51
59
  this.visited = visited;
@@ -57,7 +65,7 @@ class Arazzo1DereferenceVisitor {
57
65
  return [ancestorsLineage, directAncestors];
58
66
  }
59
67
  toBaseURI(uri) {
60
- return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
68
+ return url.resolve(this.baseURI, url.sanitize(url.stripHash(uri)));
61
69
  }
62
70
  async toReference(uri) {
63
71
  // detect maximum depth of resolution
@@ -76,6 +84,12 @@ class Arazzo1DereferenceVisitor {
76
84
  if (refSet.has(baseURI)) {
77
85
  return refSet.find(propEq(baseURI, 'uri'));
78
86
  }
87
+
88
+ // identity-based referencing: URI matches `$self` of an already processed Arazzo document
89
+ const referenceBy$self = refSet.find(identifiedBy$self(baseURI));
90
+ if (referenceBy$self !== undefined) {
91
+ return referenceBy$self;
92
+ }
79
93
  const parseResult = await parse(url.unsanitize(baseURI), {
80
94
  ...this.options,
81
95
  parse: {
@@ -279,19 +293,19 @@ class Arazzo1DereferenceVisitor {
279
293
  }
280
294
  const indirectionsSize = this.indirections.length;
281
295
  try {
282
- // compute baseURI using rules around $id and $ref keywords
296
+ // compute baseURI using rules around $self, $id and $ref keywords
283
297
  let reference = await this.toReference(url.unsanitize(this.reference.uri));
284
298
  let {
285
299
  uri: retrievalURI
286
300
  } = reference;
287
- const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
301
+ const $refBaseURI = resolveSchema$refField(this.baseURI, referencingElement);
288
302
  const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
289
303
  const file = new File({
290
304
  uri: $refBaseURIStrippedHash
291
305
  });
292
306
  const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
293
307
  const isURL = !isUnknownURI;
294
- let isInternalReference = url.stripHash(this.reference.uri) === $refBaseURI;
308
+ let isInternalReference = this.baseURI === $refBaseURI;
295
309
  let isExternalReference = !isInternalReference;
296
310
 
297
311
  // determining reference, proper evaluation and selection mechanism
@@ -318,7 +332,7 @@ class Arazzo1DereferenceVisitor {
318
332
  } else {
319
333
  // we're assuming here that we're dealing with JSON Pointer here
320
334
  retrievalURI = this.toBaseURI($refBaseURI);
321
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
335
+ isInternalReference = this.baseURI === retrievalURI;
322
336
  isExternalReference = !isInternalReference;
323
337
 
324
338
  // ignore resolving internal Schema Objects
@@ -345,7 +359,7 @@ class Arazzo1DereferenceVisitor {
345
359
  if (isURL && error instanceof EvaluationJsonSchemaUriError) {
346
360
  if (isAnchor(uriToAnchor($refBaseURI))) {
347
361
  // we're dealing with JSON Schema $anchor here
348
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
362
+ isInternalReference = this.baseURI === retrievalURI;
349
363
  isExternalReference = !isInternalReference;
350
364
 
351
365
  // ignore resolving internal Schema Objects
@@ -366,7 +380,7 @@ class Arazzo1DereferenceVisitor {
366
380
  } else {
367
381
  // we're assuming here that we're dealing with JSON Pointer here
368
382
  retrievalURI = this.toBaseURI($refBaseURI);
369
- isInternalReference = url.stripHash(this.reference.uri) === retrievalURI;
383
+ isInternalReference = this.baseURI === retrievalURI;
370
384
  isExternalReference = !isInternalReference;
371
385
 
372
386
  // ignore resolving internal Schema Objects