@speclynx/apidom-reference 6.2.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +45 -0
  3. package/dist/apidom-reference.browser.js +4701 -3478
  4. package/dist/apidom-reference.browser.min.js +1 -1
  5. package/package.json +29 -28
  6. package/src/File.cjs +5 -2
  7. package/src/File.mjs +5 -2
  8. package/src/bundle/strategies/arazzo-1/index.cjs +3 -2
  9. package/src/bundle/strategies/arazzo-1/index.mjs +3 -2
  10. package/src/bundle/strategies/asyncapi-2/index.cjs +3 -2
  11. package/src/bundle/strategies/asyncapi-2/index.mjs +3 -2
  12. package/src/bundle/strategies/openapi-2/index.cjs +3 -2
  13. package/src/bundle/strategies/openapi-2/index.mjs +3 -2
  14. package/src/bundle/strategies/openapi-3-0/index.cjs +3 -2
  15. package/src/bundle/strategies/openapi-3-0/index.mjs +3 -2
  16. package/src/bundle/strategies/openapi-3-1/index.cjs +3 -2
  17. package/src/bundle/strategies/openapi-3-1/index.mjs +3 -2
  18. package/src/dereference/strategies/DereferenceVisitor.cjs +588 -0
  19. package/src/dereference/strategies/DereferenceVisitor.mjs +580 -0
  20. package/src/dereference/strategies/arazzo-1/index.cjs +14 -39
  21. package/src/dereference/strategies/arazzo-1/index.mjs +15 -40
  22. package/src/dereference/strategies/arazzo-1/visitor.cjs +207 -331
  23. package/src/dereference/strategies/arazzo-1/visitor.mjs +215 -338
  24. package/src/dereference/strategies/asyncapi-2/index.cjs +11 -39
  25. package/src/dereference/strategies/asyncapi-2/index.mjs +11 -39
  26. package/src/dereference/strategies/asyncapi-2/visitor.cjs +133 -425
  27. package/src/dereference/strategies/asyncapi-2/visitor.mjs +137 -429
  28. package/src/dereference/strategies/openapi-2/index.cjs +12 -42
  29. package/src/dereference/strategies/openapi-2/index.mjs +12 -42
  30. package/src/dereference/strategies/openapi-2/visitor.cjs +168 -532
  31. package/src/dereference/strategies/openapi-2/visitor.mjs +172 -536
  32. package/src/dereference/strategies/openapi-3-0/index.cjs +12 -42
  33. package/src/dereference/strategies/openapi-3-0/index.mjs +12 -42
  34. package/src/dereference/strategies/openapi-3-0/visitor.cjs +194 -468
  35. package/src/dereference/strategies/openapi-3-0/visitor.mjs +199 -473
  36. package/src/dereference/strategies/openapi-3-1/index.cjs +16 -42
  37. package/src/dereference/strategies/openapi-3-1/index.mjs +16 -42
  38. package/src/dereference/strategies/openapi-3-1/visitor.cjs +350 -602
  39. package/src/dereference/strategies/openapi-3-1/visitor.mjs +357 -608
  40. package/src/dereference/strategies/overlay-1/index.cjs +3 -2
  41. package/src/dereference/strategies/overlay-1/index.mjs +3 -2
  42. package/src/index.cjs +0 -7
  43. package/src/index.mjs +0 -1
  44. package/src/parse/index.cjs +9 -1
  45. package/src/parse/index.mjs +9 -1
  46. package/src/parse/parsers/apidom-json/index.cjs +2 -1
  47. package/src/parse/parsers/apidom-json/index.mjs +2 -1
  48. package/src/parse/parsers/arazzo-json-1/index.cjs +2 -1
  49. package/src/parse/parsers/arazzo-json-1/index.mjs +2 -1
  50. package/src/parse/parsers/arazzo-yaml-1/index.cjs +2 -1
  51. package/src/parse/parsers/arazzo-yaml-1/index.mjs +2 -1
  52. package/src/parse/parsers/asyncapi-json-2/index.cjs +2 -1
  53. package/src/parse/parsers/asyncapi-json-2/index.mjs +2 -1
  54. package/src/parse/parsers/asyncapi-yaml-2/index.cjs +2 -1
  55. package/src/parse/parsers/asyncapi-yaml-2/index.mjs +2 -1
  56. package/src/parse/parsers/json/index.cjs +2 -1
  57. package/src/parse/parsers/json/index.mjs +2 -1
  58. package/src/parse/parsers/openapi-json-2/index.cjs +2 -1
  59. package/src/parse/parsers/openapi-json-2/index.mjs +2 -1
  60. package/src/parse/parsers/openapi-json-3-0/index.cjs +2 -1
  61. package/src/parse/parsers/openapi-json-3-0/index.mjs +2 -1
  62. package/src/parse/parsers/openapi-json-3-1/index.cjs +2 -1
  63. package/src/parse/parsers/openapi-json-3-1/index.mjs +2 -1
  64. package/src/parse/parsers/openapi-yaml-2/index.cjs +2 -1
  65. package/src/parse/parsers/openapi-yaml-2/index.mjs +2 -1
  66. package/src/parse/parsers/openapi-yaml-3-0/index.cjs +2 -1
  67. package/src/parse/parsers/openapi-yaml-3-0/index.mjs +2 -1
  68. package/src/parse/parsers/openapi-yaml-3-1/index.cjs +2 -1
  69. package/src/parse/parsers/openapi-yaml-3-1/index.mjs +2 -1
  70. package/src/parse/parsers/overlay-json-1/index.cjs +2 -1
  71. package/src/parse/parsers/overlay-json-1/index.mjs +2 -1
  72. package/src/parse/parsers/overlay-yaml-1/index.cjs +2 -1
  73. package/src/parse/parsers/overlay-yaml-1/index.mjs +2 -1
  74. package/src/parse/parsers/yaml-1-2/index.cjs +2 -1
  75. package/src/parse/parsers/yaml-1-2/index.mjs +2 -1
  76. package/types/apidom-reference.d.ts +0 -9
  77. package/types/dereference/strategies/DereferenceVisitor.d.ts +224 -0
  78. package/types/dereference/strategies/arazzo-1/index.d.ts +0 -1
  79. package/types/dereference/strategies/arazzo-1/visitor.d.ts +38 -38
  80. package/types/dereference/strategies/asyncapi-2/index.d.ts +0 -1
  81. package/types/dereference/strategies/asyncapi-2/visitor.d.ts +13 -29
  82. package/types/dereference/strategies/openapi-2/index.d.ts +0 -1
  83. package/types/dereference/strategies/openapi-2/visitor.d.ts +14 -29
  84. package/types/dereference/strategies/openapi-3-0/index.d.ts +0 -1
  85. package/types/dereference/strategies/openapi-3-0/visitor.d.ts +13 -28
  86. package/types/dereference/strategies/openapi-3-1/index.d.ts +0 -1
  87. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +37 -33
  88. package/types/index.d.ts +0 -1
  89. package/src/dereference/util.cjs +0 -33
  90. package/src/dereference/util.mjs +0 -27
  91. package/types/dereference/util.d.ts +0 -9
@@ -0,0 +1,588 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime-corejs3/helpers/interopRequireWildcard").default;
4
+ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.isGenericElement = exports.default = void 0;
9
+ var _ramda = require("ramda");
10
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
11
+ var _apidomCore = require("@speclynx/apidom-core");
12
+ var _apidomError = require("@speclynx/apidom-error");
13
+ var _apidomTraverse = require("@speclynx/apidom-traverse");
14
+ var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
15
+ var _UnresolvableReferenceError = _interopRequireDefault(require("../../errors/UnresolvableReferenceError.cjs"));
16
+ var _MaximumDereferenceDepthError = _interopRequireDefault(require("../../errors/MaximumDereferenceDepthError.cjs"));
17
+ var _MaximumResolveDepthError = _interopRequireDefault(require("../../errors/MaximumResolveDepthError.cjs"));
18
+ var url = _interopRequireWildcard(require("../../util/url.cjs"));
19
+ var _index = _interopRequireDefault(require("../../parse/index.cjs"));
20
+ var _Reference = _interopRequireDefault(require("../../Reference.cjs"));
21
+ const genericClasses = new Set([_apidomDatamodel.ObjectElement, _apidomDatamodel.ArrayElement, _apidomDatamodel.MemberElement, _apidomDatamodel.StringElement, _apidomDatamodel.NumberElement, _apidomDatamodel.BooleanElement, _apidomDatamodel.NullElement]);
22
+
23
+ /**
24
+ * Whether the element is generic ApiDOM: an instance of a primitive element class itself.
25
+ * Semantic elements may carry a primitive element name (e.g. a map of components named
26
+ * `object`), so the name alone doesn't tell.
27
+ * @internal
28
+ */
29
+ const isGenericElement = element => genericClasses.has(element.constructor);
30
+
31
+ /**
32
+ * A source dereferenced as one type: the source as that semantic type, with every reference
33
+ * within it transcluded. `element` exists from the start, so references leading back to
34
+ * an element still being dereferenced (cycles) can be transcluded with it.
35
+ * @internal
36
+ */
37
+
38
+ /**
39
+ * How a referencing element is transcluded with the dereferenced target of its reference.
40
+ * @internal
41
+ */
42
+
43
+ /**
44
+ * A transclusion to perform.
45
+ * @internal
46
+ */
47
+
48
+ /**
49
+ * The target of a reference: the source element, the type the reference expects, and the
50
+ * document the source belongs to. `context` is passed to the visitor dereferencing the source.
51
+ * @internal
52
+ */
53
+
54
+ /**
55
+ * State shared by all the visitors of a single dereference run.
56
+ * @internal
57
+ */
58
+
59
+ /**
60
+ * @internal
61
+ */
62
+ exports.isGenericElement = isGenericElement;
63
+ // errors and trace hops whose location is relative to the source a nested visitor traversed
64
+ const relativeLocations = new WeakSet();
65
+
66
+ /**
67
+ * Dereferences a document by the type each reference expects of its target. With the immutable
68
+ * option the documents aren't edited and the result is a new tree; otherwise they're
69
+ * dereferenced in place.
70
+ *
71
+ * Every referenced structure is dereferenced once per type the references reaching it expect.
72
+ * A source is dereferenced as a type in two steps. First it's traversed, and for each reference
73
+ * within it the target is located, dereferenced as the expected type, and recorded as
74
+ * a transclusion. Then the referencing elements are transcluded with the dereferenced targets.
75
+ * A dereferenced element is shared by all the references leading to it; a reference leading
76
+ * to an element still being dereferenced is a cycle.
77
+ *
78
+ * Strategies provide the handlers locating the targets of their references, the refraction to
79
+ * their semantic types and the visitors dereferencing the targets.
80
+ * @internal
81
+ */
82
+ class DereferenceVisitor {
83
+ reference;
84
+ options;
85
+
86
+ /**
87
+ * Base URI of the current document for resolving relative references.
88
+ */
89
+ baseURI;
90
+ run;
91
+
92
+ /**
93
+ * Map from the elements of the source the visitor traverses to their copies.
94
+ */
95
+ cloneMap;
96
+
97
+ /**
98
+ * Source the visitor traverses a refraction of, when it's dereferenced as another type.
99
+ */
100
+ refractedFrom;
101
+
102
+ /**
103
+ * Transclusions to perform, keyed by the (copy of the) referencing element.
104
+ */
105
+ transclusions = new Map();
106
+
107
+ /**
108
+ * Number of references followed to reach the source the visitor traverses.
109
+ */
110
+ depth = 0;
111
+
112
+ /**
113
+ * The source the visitor traverses, dereferenced.
114
+ */
115
+ current;
116
+ constructor({
117
+ reference,
118
+ options
119
+ }) {
120
+ this.reference = reference;
121
+ this.options = options;
122
+ this.baseURI = this.toDocumentBaseURI(reference);
123
+ this.run = {
124
+ dereferenced: new WeakMap(),
125
+ refracted: new WeakMap(),
126
+ inPlace: !options.dereference.immutable,
127
+ copies: new WeakMap(),
128
+ transcluded: new WeakSet(),
129
+ failed: new WeakSet(),
130
+ reported: new Set(),
131
+ undo: []
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Name of the strategy, for strategy specific options.
137
+ */
138
+
139
+ /**
140
+ * Creates the visitor dereferencing a source of the given document.
141
+ */
142
+
143
+ /**
144
+ * Refracts a generic element to the given type.
145
+ */
146
+
147
+ /**
148
+ * Whether the element is of the given type, so it needs no refraction.
149
+ */
150
+ isOfType(element, type) {
151
+ return element.element === type;
152
+ }
153
+
154
+ /**
155
+ * Base URI of a document: its retrieval URI unless the document identifies itself otherwise.
156
+ */
157
+ toDocumentBaseURI(reference) {
158
+ return reference.uri;
159
+ }
160
+
161
+ /**
162
+ * A document already loaded in the run identified by the URI other than by its retrieval URI.
163
+ */
164
+ findReferenceByIdentity(_uri) {
165
+ return undefined;
166
+ }
167
+
168
+ /**
169
+ * Dereferences the element, which belongs to the document of the visitor's reference.
170
+ * With the immutable option the element isn't edited and its dereferenced copy is returned;
171
+ * otherwise it's dereferenced in place and returned.
172
+ */
173
+ async dereference(element, context) {
174
+ // frozen elements are left as they are: they're dereferenced as with the immutable option
175
+ const isFrozen = value => (0, _apidomDatamodel.isElement)(value) && (value.isFrozen || value instanceof _apidomDatamodel.ParseResultElement && value.result?.isFrozen === true);
176
+ if (this.run.inPlace && (isFrozen(element) || this.reference.refSet?.refs.some(ref => isFrozen(ref.value)))) {
177
+ this.run = {
178
+ ...this.run,
179
+ inPlace: false
180
+ };
181
+ }
182
+ const dereferenced = await this.dereferenceAs({
183
+ source: element,
184
+ type: element.element,
185
+ reference: this.reference,
186
+ context
187
+ }, 0);
188
+ return dereferenced.element;
189
+ }
190
+ toBaseURI(uri) {
191
+ return url.resolve(this.baseURI, url.sanitize(url.stripHash(uri)));
192
+ }
193
+ async toReference(uri) {
194
+ // detect maximum depth of resolution
195
+ if (this.reference.depth >= this.options.resolve.maxDepth) {
196
+ throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
197
+ maxDepth: this.options.resolve.maxDepth,
198
+ uri: this.reference.uri
199
+ });
200
+ }
201
+ const baseURI = this.toBaseURI(uri);
202
+ const {
203
+ refSet
204
+ } = this.reference;
205
+
206
+ // we've already processed this Reference in past
207
+ if (refSet.has(baseURI)) {
208
+ return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
209
+ }
210
+ const referenceByIdentity = this.findReferenceByIdentity(baseURI);
211
+ if (referenceByIdentity !== undefined) {
212
+ return referenceByIdentity;
213
+ }
214
+ const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
215
+ ...this.options,
216
+ parse: {
217
+ ...this.options.parse,
218
+ mediaType: 'text/plain'
219
+ }
220
+ });
221
+
222
+ // registered as parsed: with the immutable option it's only ever copied, never edited
223
+ const reference = new _Reference.default({
224
+ uri: baseURI,
225
+ value: parseResult,
226
+ depth: this.reference.depth + 1
227
+ });
228
+ refSet.add(reference);
229
+ return reference;
230
+ }
231
+
232
+ /**
233
+ * Handles an error according to the continueOnError option.
234
+ *
235
+ * For new errors: wraps in UnresolvableReferenceError with structured context
236
+ * (type, uri, location, codeFrame, refFieldName, refFieldValue, trace).
237
+ * For errors already wrapped by a nested visitor: prepends the current hop to the trace.
238
+ *
239
+ * Inner/intermediate visitors always throw to let the trace accumulate.
240
+ * Only the entry document visitor respects continueOnError (callback/swallow/throw),
241
+ * and reports the failure of a referencing element once.
242
+ */
243
+ handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
244
+ const {
245
+ continueOnError
246
+ } = this.options.dereference;
247
+ const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
248
+ const uri = this.reference.uri;
249
+ const type = referencingElement.element;
250
+ const codeFrame = (0, _apidomCore.toYAML)(referencingElement);
251
+
252
+ // find element location by identity in the document tree.
253
+ // guarded: this.reference.value may not be a ParseResultElement or may lack a result.
254
+ // falls back to visitorPath, which is relative to the source being traversed
255
+ let location;
256
+ let refractedFromLocation;
257
+ const root = this.reference.value.result;
258
+ if ((0, _apidomDatamodel.isElement)(root)) {
259
+ (0, _apidomTraverse.traverse)(root, {
260
+ enter: p => {
261
+ if (p.node === referencingElement) {
262
+ location = p.formatPath();
263
+ p.stop();
264
+ } else if (p.node === this.refractedFrom) {
265
+ refractedFromLocation = p.formatPath();
266
+ } else if (this.isTranscluded(p.node)) {
267
+ p.skip();
268
+ }
269
+ }
270
+ });
271
+ }
272
+ // not found, the location is relative to the traversed source: an enclosing visitor prefixes it
273
+ const isRelative = location === undefined && refractedFromLocation === undefined;
274
+ // within a refraction, the visitor path is relative to the refracted source
275
+ location ??= (refractedFromLocation ?? '') + visitorPath.formatPath();
276
+ const hop = {
277
+ uri,
278
+ type,
279
+ refFieldName,
280
+ refFieldValue,
281
+ location,
282
+ codeFrame
283
+ };
284
+ if (isRelative) relativeLocations.add(hop);
285
+
286
+ // enrich existing error from nested visitor or create new one
287
+ let unresolvedError;
288
+ if (error instanceof _UnresolvableReferenceError.default) {
289
+ // prefix relative locations for entries belonging to the referenced document
290
+ const refBaseURI = this.toBaseURI(refFieldValue);
291
+ const fragment = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference(refFieldValue);
292
+ if (fragment) {
293
+ for (const entry of [error, ...error.trace]) {
294
+ if (relativeLocations.has(entry) && entry.uri === refBaseURI && entry.location) {
295
+ entry.location = fragment + entry.location;
296
+ relativeLocations.delete(entry);
297
+ }
298
+ }
299
+ }
300
+ // @ts-ignore
301
+ error.trace = [hop, ...error.trace];
302
+ unresolvedError = error;
303
+ } else {
304
+ unresolvedError = new _UnresolvableReferenceError.default(message, {
305
+ cause: error,
306
+ type,
307
+ uri,
308
+ location,
309
+ codeFrame,
310
+ refFieldName,
311
+ refFieldValue,
312
+ trace: []
313
+ });
314
+ if (isRelative) relativeLocations.add(unresolvedError);
315
+ }
316
+ if (!isEntryDocument || continueOnError === false) throw unresolvedError;
317
+ // a referencing element is traversed again by every source containing it: its failure is reported once
318
+ this.run.failed.add(referencingElement);
319
+ const failure = JSON.stringify([uri, location, refFieldName, refFieldValue]);
320
+ if (this.run.reported.has(failure)) return;
321
+ this.run.reported.add(failure);
322
+ if (typeof continueOnError === 'function') continueOnError(unresolvedError);
323
+ }
324
+
325
+ /**
326
+ * The source as the given semantic type: the source itself when it's of that type, otherwise
327
+ * its refraction to that type, made once per dereference run. The refraction is made from
328
+ * the source dereferenced as its own type, so it doesn't depend on which reference
329
+ * reaches the source first.
330
+ */
331
+ async toSemantic(target, depth) {
332
+ const {
333
+ source,
334
+ type
335
+ } = target;
336
+ if (this.isOfType(source, type)) return source;
337
+ let byType = this.run.refracted.get(source);
338
+ if (byType === undefined) {
339
+ byType = new Map();
340
+ this.run.refracted.set(source, byType);
341
+ }
342
+ const cached = byType.get(type);
343
+ if (cached !== undefined) return cached;
344
+ const dereferenced = isGenericElement(source) ? source : (await this.dereferenceAs({
345
+ ...target,
346
+ type: source.element
347
+ }, depth)).element;
348
+ // dereferencing the source may have led back to this very refraction
349
+ const reached = byType.get(type);
350
+ if (reached !== undefined) return reached;
351
+ const semantic = this.refractTo((0, _apidomCore.toGeneric)(dereferenced), type);
352
+ byType.set(type, semantic);
353
+ this.run.undo.push(() => byType.delete(type));
354
+ return semantic;
355
+ }
356
+
357
+ /**
358
+ * Dereferences the source as the given type, once per dereference run. `depth` is the number
359
+ * of references followed to reach the source.
360
+ */
361
+ async dereferenceAs(target, depth) {
362
+ const {
363
+ source,
364
+ type,
365
+ reference,
366
+ context
367
+ } = target;
368
+ let byType = this.run.dereferenced.get(source);
369
+ if (byType === undefined) {
370
+ byType = new Map();
371
+ this.run.dereferenced.set(source, byType);
372
+ }
373
+ const existing = byType.get(type);
374
+ if (existing !== undefined) return existing;
375
+ const semantic = await this.toSemantic(target, depth);
376
+ // a refraction is made for the run, so it's dereferenced as is
377
+ const isOwned = semantic !== source || this.run.inPlace;
378
+ const cloneMap = isOwned ? undefined : this.run.copies;
379
+ const dereferenced = {
380
+ element: isOwned ? semantic : (0, _apidomDatamodel.cloneDeep)(semantic, {
381
+ visited: cloneMap
382
+ }),
383
+ done: false,
384
+ height: 0,
385
+ alias: false,
386
+ unresolved: false
387
+ };
388
+ // a failure discards whatever got dereferenced or refracted meanwhile, as it may lead to this element
389
+ const undoSize = this.run.undo.length;
390
+ byType.set(type, dereferenced);
391
+ this.run.undo.push(() => byType.delete(type));
392
+ const visitor = this.createVisitor(reference, context);
393
+ visitor.run = this.run;
394
+ visitor.cloneMap = cloneMap;
395
+ visitor.refractedFrom = semantic !== source ? source : undefined;
396
+ visitor.depth = depth;
397
+ visitor.current = dereferenced;
398
+ try {
399
+ // the handlers only record transclusions
400
+ await (0, _apidomTraverse.traverseAsync)(semantic, visitor);
401
+ } catch (error) {
402
+ this.run.undo.splice(undoSize).reverse().forEach(undo => undo());
403
+ throw error;
404
+ }
405
+ for (const transclusion of visitor.transclusions.values()) {
406
+ if (!transclusion.back && !transclusion.attachment) {
407
+ dereferenced.height = Math.max(dereferenced.height, transclusion.target.height + 1);
408
+ }
409
+ }
410
+ const rootTransclusion = visitor.transclusions.get(dereferenced.element);
411
+ dereferenced.unresolved = rootTransclusion !== undefined && (rootTransclusion.keep || rootTransclusion.target.unresolved);
412
+ dereferenced.element = visitor.transclude(dereferenced.element);
413
+ dereferenced.done = true;
414
+ return dereferenced;
415
+ }
416
+
417
+ /**
418
+ * Dereferences the target of a reference as the expected type and records the transclusion.
419
+ */
420
+ async addTransclusion(path, target, spec) {
421
+ const referencingCopy = this.cloneMap?.get(path.node) ?? path.node;
422
+ const {
423
+ maxDepth
424
+ } = this.options.dereference;
425
+
426
+ // the source being traversed is itself a referencing element: an alias of the target
427
+ if (path.parentPath === null && this.current !== undefined) this.current.alias = true;
428
+
429
+ // detect maximum depth of dereferencing
430
+ if (this.depth + 1 > maxDepth) {
431
+ throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
432
+ maxDepth,
433
+ uri: this.reference.uri
434
+ });
435
+ }
436
+ const existing = this.run.dereferenced.get(target.source)?.get(target.type);
437
+
438
+ // a target dereferenced earlier counts the references followed within it as well
439
+ if (existing?.done && this.depth + 1 + existing.height > maxDepth) {
440
+ throw new _MaximumDereferenceDepthError.default(`Maximum dereference depth of "${maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
441
+ maxDepth,
442
+ uri: this.reference.uri
443
+ });
444
+ }
445
+
446
+ // a reference leading to an element still being dereferenced is a cycle
447
+ if (existing !== undefined && !existing.done) {
448
+ this.reference.refSet.circular = true;
449
+ if (this.options.dereference.circular === 'error') {
450
+ throw new _apidomError.ApiDOMStructuredError('Circular reference detected', spec.circular.details);
451
+ }
452
+ // an alias has no value before its own reference is transcluded: the referencing element
453
+ // is kept, to be transcluded by an enclosing dereferenced element, if any
454
+ const keep = existing.alias && this.options.dereference.circular === 'ignore';
455
+ this.transclusions.set(referencingCopy, {
456
+ ...spec,
457
+ target: existing,
458
+ back: true,
459
+ keep,
460
+ attachment: false
461
+ });
462
+ return;
463
+ }
464
+ const dereferenced = existing ?? (await this.dereferenceAs(target, this.depth + 1));
465
+ // the referencing element failed while its target was dereferenced, e.g. as a circular reference
466
+ if (this.run.failed.has(path.node)) return;
467
+ this.transclusions.set(referencingCopy, {
468
+ ...spec,
469
+ target: dereferenced,
470
+ back: false,
471
+ keep: false,
472
+ attachment: false
473
+ });
474
+ }
475
+
476
+ /**
477
+ * Records the transclusion of a referencing element with a target that isn't dereferenced:
478
+ * the target as the expected type is attached as it is, e.g. an Operation Object to the Link
479
+ * Object pointing at it. Attached targets never form cycles.
480
+ */
481
+ async addAttachment(path, target, toTranscludedElement) {
482
+ const referencingCopy = this.cloneMap?.get(path.node) ?? path.node;
483
+ const semantic = await this.toSemantic(target, this.depth + 1);
484
+ // with the immutable option, the attached target is its copy in the result when it has one
485
+ const element = this.run.inPlace ? semantic : this.run.copies.get(semantic) ?? semantic;
486
+ this.transclusions.set(referencingCopy, {
487
+ toTranscludedElement,
488
+ circular: {
489
+ uri: '',
490
+ meta: {},
491
+ details: {}
492
+ },
493
+ target: {
494
+ element,
495
+ done: true,
496
+ height: 0,
497
+ alias: false,
498
+ unresolved: false
499
+ },
500
+ back: false,
501
+ keep: false,
502
+ attachment: true
503
+ });
504
+ }
505
+
506
+ /**
507
+ * Transcludes every referencing element within the element with its dereferenced target.
508
+ */
509
+ transclude(element) {
510
+ if (this.transclusions.size === 0) return element;
511
+
512
+ // replaced on leave: nested referencing elements first, so merges see them replaced
513
+ (0, _apidomTraverse.traverse)(element, {
514
+ enter: path => {
515
+ if (this.isTranscluded(path.node)) path.skip();
516
+ },
517
+ leave: path => {
518
+ const transclusion = this.transclusions.get(path.node);
519
+ if (path.node !== element && transclusion !== undefined) {
520
+ const transcludedElement = this.toTranscludedElement(path.node, transclusion);
521
+ // a kept referencing element is left to be transcluded by an enclosing element
522
+ if (transcludedElement === path.node) return;
523
+ this.run.transcluded.add(transcludedElement);
524
+ path.replaceWith(transcludedElement);
525
+ }
526
+ }
527
+ }, {
528
+ mutable: true
529
+ });
530
+ const rootTransclusion = this.transclusions.get(element);
531
+ if (rootTransclusion === undefined) return element;
532
+
533
+ // references leading back to the element may already be transcluded with it, so it's filled in place
534
+ const transcludedElement = this.toTranscludedElement(element, rootTransclusion);
535
+ if (transcludedElement === element) return element;
536
+ if (transcludedElement.constructor !== element.constructor) return transcludedElement;
537
+ element.element = transcludedElement.element;
538
+ element.content = transcludedElement.content;
539
+ element.meta = transcludedElement.meta;
540
+ element.attributes = transcludedElement.attributes;
541
+ return element;
542
+ }
543
+
544
+ /**
545
+ * The element transcluded in place of a referencing element.
546
+ */
547
+ toTranscludedElement(referencingElement, transclusion) {
548
+ // the referencing element waits for an enclosing element to transclude it
549
+ if (transclusion.keep) return referencingElement;
550
+ // an alias whose reference never reaches a value
551
+ if (transclusion.target.unresolved) {
552
+ const {
553
+ aliasField
554
+ } = transclusion;
555
+ const alias = transclusion.target.element;
556
+ if (aliasField === undefined || !(0, _apidomDatamodel.isObjectElement)(alias)) return referencingElement;
557
+ const fields = (0, _apidomDatamodel.cloneShallow)(alias);
558
+ fields.remove(aliasField);
559
+ return transclusion.toTranscludedElement(referencingElement, fields);
560
+ }
561
+ if (transclusion.back && this.options.dereference.circular === 'replace') {
562
+ const refElement = new _apidomDatamodel.RefElement(transclusion.circular.uri, {
563
+ type: referencingElement.element,
564
+ ...transclusion.circular.meta
565
+ });
566
+ const replacer = this.options.dereference.strategyOpts[this.strategyName]?.circularReplacer ?? this.options.dereference.circularReplacer;
567
+ return replacer(refElement);
568
+ }
569
+ return transclusion.toTranscludedElement(referencingElement, transclusion.target.element);
570
+ }
571
+
572
+ /**
573
+ * Whether the element is transcluded in place of a referencing element, in this run or,
574
+ * when dereferencing in place, in an earlier one: transcluded elements carry the `ref-type` meta,
575
+ * which only dereferencing sets (bundling annotates hoisted elements with `ref-origin` only).
576
+ */
577
+ isTranscluded(element) {
578
+ return this.run.transcluded.has(element) || !element.isMetaEmpty && element.meta.hasKey('ref-type');
579
+ }
580
+
581
+ /**
582
+ * Dereferenced in place, a source may already hold transcluded elements.
583
+ */
584
+ enter(path) {
585
+ if (this.isTranscluded(path.node)) path.skip();
586
+ }
587
+ }
588
+ var _default = exports.default = DereferenceVisitor;