@speclynx/apidom-reference 4.12.0 → 4.13.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.
@@ -0,0 +1,567 @@
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.default = void 0;
9
+ var _ramda = require("ramda");
10
+ var _apidomDatamodel = require("@speclynx/apidom-datamodel");
11
+ var _apidomCore = require("@speclynx/apidom-core");
12
+ var _apidomTraverse = require("@speclynx/apidom-traverse");
13
+ var _apidomJsonPointer = require("@speclynx/apidom-json-pointer");
14
+ var _apidomNsOpenapi = require("@speclynx/apidom-ns-openapi-2");
15
+ var _BundleError = _interopRequireDefault(require("../../../errors/BundleError.cjs"));
16
+ var _UnresolvableReferenceError = _interopRequireDefault(require("../../../errors/UnresolvableReferenceError.cjs"));
17
+ var _MaximumBundleDepthError = _interopRequireDefault(require("../../../errors/MaximumBundleDepthError.cjs"));
18
+ var _MaximumResolveDepthError = _interopRequireDefault(require("../../../errors/MaximumResolveDepthError.cjs"));
19
+ var url = _interopRequireWildcard(require("../../../util/url.cjs"));
20
+ var _index = _interopRequireDefault(require("../../../parse/index.cjs"));
21
+ var _Reference = _interopRequireDefault(require("../../../Reference.cjs"));
22
+ var _util = require("../../util.cjs");
23
+ /**
24
+ * Maps the `referenced-element` meta value (set during refraction) to the
25
+ * top-level Swagger Object field where the referenced element should be
26
+ * hoisted. Unlike OpenAPI 3.x, OpenAPI 2.0 has no single Components Object;
27
+ * reusable definitions live directly under the document root. Field names are
28
+ * sourced from the Swagger Object fixed fields so they cannot drift from the
29
+ * namespace definition.
30
+ */
31
+ const sf = (0, _apidomCore.fixedFields)(_apidomNsOpenapi.SwaggerElement, {
32
+ indexed: true
33
+ });
34
+ const fieldByReferencedElement = {
35
+ schema: sf.definitions.name,
36
+ parameter: sf.parameters.name,
37
+ response: sf.responses.name
38
+ };
39
+
40
+ /**
41
+ * Maps a top-level Swagger Object field to the named-content element the
42
+ * namespace uses for it, so a bundle-created field matches a parsed/refracted
43
+ * one (e.g. carries the `definitions` class) rather than being a generic
44
+ * object.
45
+ */
46
+ const fieldElementByField = {
47
+ [sf.definitions.name]: _apidomNsOpenapi.DefinitionsElement,
48
+ [sf.parameters.name]: _apidomNsOpenapi.ParametersDefinitionsElement,
49
+ [sf.responses.name]: _apidomNsOpenapi.ResponsesDefinitionsElement
50
+ };
51
+
52
+ /**
53
+ * @public
54
+ */
55
+
56
+ /**
57
+ * @public
58
+ */
59
+ class OpenAPI2BundleVisitor {
60
+ reference;
61
+ options;
62
+
63
+ /**
64
+ * The base URI (without hash) of the entry document. A reference is
65
+ * considered internal when it resolves to this URI and external otherwise.
66
+ * Derived from the shared refSet's root reference.
67
+ */
68
+ get entryURI() {
69
+ return url.stripHash(this.reference.refSet?.rootRef?.uri ?? '');
70
+ }
71
+
72
+ /**
73
+ * The entry parse result. Derived from the shared refSet's root reference.
74
+ */
75
+ get entryParseResult() {
76
+ return this.reference.refSet.rootRef.value;
77
+ }
78
+
79
+ /**
80
+ * The entry document element. Hoisted external fragments are placed into its
81
+ * top-level definition fields. The `canBundle` guard guarantees it is a
82
+ * SwaggerElement.
83
+ */
84
+ get entryResult() {
85
+ return this.entryParseResult.result;
86
+ }
87
+
88
+ /**
89
+ * Shared across the entry document and every external document visitor.
90
+ * Guarantees each external fragment is hoisted exactly once and gives us a
91
+ * stable internal pointer to rewrite every referencing element to.
92
+ */
93
+ assignments;
94
+
95
+ /**
96
+ * Component names already reserved per definition field. Reserved before
97
+ * recursion so collision suffixing (`Pet`, `Pet-2`, ...) accounts for
98
+ * components that are still being bundled.
99
+ */
100
+ reservedNames;
101
+
102
+ /**
103
+ * Caches refracted fragments keyed by source element and target element type.
104
+ * The type is part of the key because the same source can be refracted into
105
+ * different semantic elements depending on the referencing context (e.g. a
106
+ * generic object referenced once as a Parameter and once as a Response).
107
+ */
108
+ refractCache;
109
+
110
+ /**
111
+ * Canonical URIs of Path Item Objects currently being inlined. Path Items
112
+ * are not a top-level definition field in OpenAPI 2.0, so the `assignments`
113
+ * map can't break their cycles. This stack guards against circular external
114
+ * Path Item references.
115
+ */
116
+ inlineStack;
117
+ constructor({
118
+ reference,
119
+ options,
120
+ assignments = new Map(),
121
+ reservedNames = new Map(),
122
+ refractCache = new WeakMap(),
123
+ inlineStack = new Set()
124
+ }) {
125
+ this.reference = reference;
126
+ this.options = options;
127
+ this.inlineStack = inlineStack;
128
+ this.assignments = assignments;
129
+ this.reservedNames = reservedNames;
130
+ this.refractCache = refractCache;
131
+ }
132
+ getRefracted(source, type) {
133
+ return this.refractCache.get(source)?.get(type);
134
+ }
135
+ setRefracted(source, type, refracted) {
136
+ let byType = this.refractCache.get(source);
137
+ if (byType === undefined) {
138
+ byType = new Map();
139
+ this.refractCache.set(source, byType);
140
+ }
141
+ byType.set(type, refracted);
142
+ }
143
+ toBaseURI(uri) {
144
+ return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
145
+ }
146
+ async toReference(uri) {
147
+ // detect maximum depth of resolution
148
+ if (this.reference.depth >= this.options.resolve.maxDepth) {
149
+ throw new _MaximumResolveDepthError.default(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
150
+ maxDepth: this.options.resolve.maxDepth,
151
+ uri: this.reference.uri
152
+ });
153
+ }
154
+ const baseURI = this.toBaseURI(uri);
155
+ const {
156
+ refSet
157
+ } = this.reference;
158
+
159
+ // we've already processed this Reference in past
160
+ if (refSet.has(baseURI)) {
161
+ return refSet.find((0, _ramda.propEq)(baseURI, 'uri'));
162
+ }
163
+ const parseResult = await (0, _index.default)(url.unsanitize(baseURI), {
164
+ ...this.options,
165
+ parse: {
166
+ ...this.options.parse,
167
+ mediaType: 'text/plain'
168
+ }
169
+ });
170
+ const reference = new _Reference.default({
171
+ uri: baseURI,
172
+ value: parseResult,
173
+ depth: this.reference.depth + 1
174
+ });
175
+ refSet.add(reference);
176
+ return reference;
177
+ }
178
+
179
+ /**
180
+ * Normalizes a self-file reference (e.g. `./root.json#/definitions/Pet`)
181
+ * to a bare fragment (`#/definitions/Pet`) so the bundled document stays
182
+ * transferable. Bare fragments are returned unchanged.
183
+ */
184
+ normalizeSelfFileRef(ref) {
185
+ return ref.startsWith('#') ? ref : url.getHash(ref);
186
+ }
187
+
188
+ /**
189
+ * Handles an error according to the `bundle.continueOnError` option.
190
+ *
191
+ * For new errors: wraps in UnresolvableReferenceError with structured
192
+ * context. For errors already wrapped by a nested visitor: prepends the
193
+ * current hop to the trace.
194
+ *
195
+ * Inner/intermediate visitors always throw to let the trace accumulate. Only
196
+ * the entry document visitor respects continueOnError (callback/swallow/throw).
197
+ */
198
+ handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
199
+ // a collision reported as `error` is a deliberate hard stop with no
200
+ // resolution-failure analog, so pass it through unchanged. A maxDepth
201
+ // breach (MaximumBundleDepthError, which also extends BundleError) is a
202
+ // resolution failure and flows through below — wrapped and subject to
203
+ // continueOnError, like dereferencing.
204
+ if (error instanceof _BundleError.default && !(error instanceof _MaximumBundleDepthError.default)) {
205
+ throw error;
206
+ }
207
+ const {
208
+ continueOnError
209
+ } = this.options.bundle;
210
+ const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
211
+ const uri = this.reference.uri;
212
+ const type = referencingElement.element;
213
+ const codeFrame = (0, _apidomCore.toYAML)(referencingElement);
214
+ const location = visitorPath.formatPath();
215
+ const hop = {
216
+ uri,
217
+ type,
218
+ refFieldName,
219
+ refFieldValue,
220
+ location,
221
+ codeFrame
222
+ };
223
+ let unresolvedError;
224
+ if (error instanceof _UnresolvableReferenceError.default) {
225
+ // @ts-ignore
226
+ error.trace = [hop, ...error.trace];
227
+ unresolvedError = error;
228
+ } else {
229
+ unresolvedError = new _UnresolvableReferenceError.default(message, {
230
+ cause: error,
231
+ type,
232
+ uri,
233
+ location,
234
+ codeFrame,
235
+ refFieldName,
236
+ refFieldValue,
237
+ trace: []
238
+ });
239
+ }
240
+ if (!isEntryDocument || continueOnError === false) throw unresolvedError;
241
+ if (typeof continueOnError === 'function') continueOnError(unresolvedError);
242
+ }
243
+
244
+ /**
245
+ * Lazily creates the requested top-level definition field on the entry
246
+ * document, returning the field where a fragment is hoisted.
247
+ */
248
+ ensureField(field) {
249
+ const entryResult = this.entryResult;
250
+ let fieldElement = entryResult.get(field);
251
+ if (!(0, _apidomDatamodel.isObjectElement)(fieldElement)) {
252
+ const FieldElement = fieldElementByField[field] ?? _apidomDatamodel.ObjectElement;
253
+ fieldElement = new FieldElement();
254
+ entryResult.set(field, fieldElement);
255
+ }
256
+ return fieldElement;
257
+ }
258
+
259
+ /**
260
+ * Derives a component name from the referenced JSON Pointer's last token,
261
+ * falling back to the referenced file's basename.
262
+ */
263
+ basenameOf(jsonPointer, baseURI) {
264
+ const tokens = (0, _apidomJsonPointer.parse)(jsonPointer).tree;
265
+ let candidate = tokens.length > 0 ? tokens[tokens.length - 1] : '';
266
+ if (candidate === '') {
267
+ const lastSlash = baseURI.lastIndexOf('/');
268
+ const lastDot = baseURI.lastIndexOf('.');
269
+ const fileName = baseURI.slice(lastSlash + 1);
270
+ // strip the extension only when the last dot belongs to the file name
271
+ candidate = lastDot > lastSlash ? baseURI.slice(lastSlash + 1, lastDot) : fileName;
272
+ }
273
+ if (candidate === '') candidate = 'Schema';
274
+ return candidate;
275
+ }
276
+
277
+ /**
278
+ * Computes the base component name (before collision suffixing) according to
279
+ * the configured naming strategy.
280
+ */
281
+ baseName(element, field, jsonPointer, baseURI) {
282
+ // strategy specific options take precedence over the top-level bundle options
283
+ const componentNamesStrategy = this.options.bundle.strategyOpts['openapi-2']?.componentNamesStrategy ?? this.options.bundle.componentNamesStrategy;
284
+ if (typeof componentNamesStrategy === 'function') {
285
+ const resolved = componentNamesStrategy({
286
+ element,
287
+ field,
288
+ jsonPointer,
289
+ baseURI
290
+ });
291
+ if (typeof resolved === 'string' && resolved !== '') {
292
+ return resolved;
293
+ }
294
+ return this.basenameOf(jsonPointer, baseURI);
295
+ }
296
+ if (componentNamesStrategy === 'title') {
297
+ const title = (0, _apidomDatamodel.isObjectElement)(element) ? (0, _apidomCore.toValue)(element.get('title')) : undefined;
298
+ if (typeof title === 'string' && title.trim() !== '') {
299
+ const name = (0, _util.sanitizeComponentName)((0, _util.toPascalCase)(title));
300
+ if (name !== '') return name;
301
+ }
302
+ // fall back to basename when no usable title is present
303
+ }
304
+ return this.basenameOf(jsonPointer, baseURI);
305
+ }
306
+
307
+ /**
308
+ * Computes a collision-free component name within the target field. A name is
309
+ * taken if it's already placed in the field or reserved by a component that
310
+ * is still being bundled (reserved before recursion).
311
+ */
312
+ uniqueName(candidate, field) {
313
+ const fieldElement = this.ensureField(field);
314
+ const reserved = this.reservedNames.get(field) ?? new Set();
315
+ return (0, _util.uniqueName)(candidate, name => fieldElement.hasKey(name) || reserved.has(name));
316
+ }
317
+
318
+ /**
319
+ * Bundles a Reference Object (parameters/responses) or a JSON Reference
320
+ * Object (schemas). The two reference flavors differ only in their predicate
321
+ * and refraction helpers, so the shared logic is parameterized here.
322
+ */
323
+ async bundleReference(path, referencingElement, isSemanticReference, isReferenceLike, refractReferenceFn, label) {
324
+ const $ref = (0, _apidomCore.toValue)(referencingElement.$ref);
325
+ const retrievalURI = this.toBaseURI($ref);
326
+ const isInternalReference = this.entryURI === retrievalURI;
327
+ const isExternalReference = !isInternalReference;
328
+ if (isInternalReference) {
329
+ // normalize self-file references to a bare fragment so the bundled
330
+ // document stays transferable; bare fragments are left untouched
331
+ referencingElement.set('$ref', this.normalizeSelfFileRef($ref));
332
+ path.skip();
333
+ return;
334
+ }
335
+
336
+ // honor the resolve.external switch
337
+ if (!this.options.resolve.external && isExternalReference) {
338
+ path.skip();
339
+ return;
340
+ }
341
+ const $refBaseURI = url.resolve(retrievalURI, $ref);
342
+ const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
343
+
344
+ // the target field depends on the referencing context, so it's part of the
345
+ // identity: the same external target referenced as e.g. both a parameter and
346
+ // a response must be hoisted into both fields. The refractor sets
347
+ // `referenced-element` for every Reference Object position; the `schema`
348
+ // fallback is a defensive default for the (unexpected) untagged case.
349
+ const referencedElementType = referencingElement.meta.get('referenced-element');
350
+ const field = fieldByReferencedElement[referencedElementType] ?? fieldByReferencedElement.schema;
351
+ const canonicalKey = `${field}\t${retrievalURI}#${jsonPointer}`;
352
+
353
+ // already hoisted (or being hoisted) — just rewrite the pointer.
354
+ //
355
+ // this is also how circular references terminate: the assignment is
356
+ // reserved BEFORE recursing into the fragment (see below), so a reference
357
+ // that cycles back (directly or indirectly) finds its pointer already
358
+ // reserved here and is rewritten without recursing. Cycles are deliberately
359
+ // preserved as internal `$ref`s pointing at each other — that is valid,
360
+ // serializable output; resolving the cycle (if desired) is dereferencing's
361
+ // job, not bundling's.
362
+ if (this.assignments.has(canonicalKey)) {
363
+ referencingElement.set('$ref', this.assignments.get(canonicalKey));
364
+ path.skip();
365
+ return;
366
+ }
367
+ try {
368
+ // detect maximum depth of bundling
369
+ if (this.reference.depth >= this.options.bundle.maxDepth) {
370
+ throw new _MaximumBundleDepthError.default(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
371
+ maxDepth: this.options.bundle.maxDepth,
372
+ uri: this.reference.uri
373
+ });
374
+ }
375
+ const reference = await this.toReference($ref);
376
+
377
+ // possibly non-semantic fragment
378
+ let referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
379
+
380
+ // apply semantics to the fragment so nested references become Reference Objects
381
+ if (referencedElement.element !== referencedElementType && !isSemanticReference(referencedElement)) {
382
+ const cached = this.getRefracted(referencedElement, referencedElementType);
383
+ if (cached !== undefined) {
384
+ referencedElement = cached;
385
+ } else if (isReferenceLike(referencedElement)) {
386
+ const sourceElement = referencedElement;
387
+ referencedElement = refractReferenceFn(referencedElement);
388
+ referencedElement.meta.set('referenced-element', referencedElementType);
389
+ this.setRefracted(sourceElement, referencedElementType, referencedElement);
390
+ } else {
391
+ const sourceElement = referencedElement;
392
+ referencedElement = (0, _apidomNsOpenapi.refract)(referencedElement, {
393
+ element: referencedElementType
394
+ });
395
+ this.setRefracted(sourceElement, referencedElementType, referencedElement);
396
+ }
397
+ }
398
+ const preferredName = this.baseName(referencedElement, field, jsonPointer, retrievalURI);
399
+ const componentName = this.uniqueName(preferredName, field);
400
+ const internalPointer = `#/${field}/${(0, _apidomJsonPointer.escape)(componentName)}`;
401
+
402
+ // a rename means two distinct targets resolved to the same name; each
403
+ // keeps its own component (and origin), so report the rename per severity.
404
+ // strategy specific options take precedence over the top-level bundle options
405
+ const onComponentNameCollision = this.options.bundle.strategyOpts['openapi-2']?.onComponentNameCollision ?? this.options.bundle.onComponentNameCollision;
406
+ if (componentName !== preferredName && onComponentNameCollision !== 'off') {
407
+ const message = `Component "${preferredName}" in ${field} is referenced with the same name but different content. Renamed to "${componentName}".`;
408
+ if (onComponentNameCollision === 'error') {
409
+ throw new _BundleError.default(message);
410
+ }
411
+ const annotation = new _apidomDatamodel.AnnotationElement(message);
412
+ annotation.classes.push('warning');
413
+ annotation.code = 'bundle-component-name-collision';
414
+ // append (never prepend) so the result element stays ahead of annotations
415
+ this.entryParseResult.content.push(annotation);
416
+ }
417
+
418
+ // reserve the assignment and component name before recursing so cyclic
419
+ // references terminate
420
+ this.assignments.set(canonicalKey, internalPointer);
421
+ if (!this.reservedNames.has(field)) this.reservedNames.set(field, new Set());
422
+ this.reservedNames.get(field).add(componentName);
423
+
424
+ // own a copy of the fragment and bundle its own external references
425
+ const hoistedElement = (0, _apidomDatamodel.cloneDeep)(referencedElement);
426
+ const visitor = new OpenAPI2BundleVisitor({
427
+ reference,
428
+ options: this.options,
429
+ assignments: this.assignments,
430
+ reservedNames: this.reservedNames,
431
+ refractCache: this.refractCache,
432
+ inlineStack: this.inlineStack
433
+ });
434
+ const bundledElement = await (0, _apidomTraverse.traverseAsync)(hoistedElement, visitor, {
435
+ mutable: true
436
+ });
437
+
438
+ // annotate the hoisted fragment with info about its origin
439
+ if ((0, _apidomDatamodel.isElement)(bundledElement)) {
440
+ bundledElement.meta.set('ref-origin', reference.uri);
441
+ }
442
+
443
+ // place the bundled fragment into the entry document's definitions
444
+ this.ensureField(field).set(componentName, bundledElement);
445
+
446
+ // rewrite the referencing element to point at the hoisted fragment
447
+ referencingElement.set('$ref', internalPointer);
448
+ path.skip();
449
+ } catch (error) {
450
+ this.handleError(`Error while bundling ${label}. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
451
+ path.skip();
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Reference Object covers reusable Parameter and Response Objects, which are
457
+ * hoisted into the top-level `parameters` and `responses` fields.
458
+ */
459
+ async ReferenceElement(path) {
460
+ await this.bundleReference(path, path.node, _apidomNsOpenapi.isReferenceElement, _apidomNsOpenapi.isReferenceLikeElement, _apidomNsOpenapi.refractReference, 'Reference Object');
461
+ }
462
+
463
+ /**
464
+ * JSON Reference Object covers reusable Schema Objects, which are hoisted
465
+ * into the top-level `definitions` field.
466
+ */
467
+ async JSONReferenceElement(path) {
468
+ await this.bundleReference(path, path.node, _apidomNsOpenapi.isJSONReferenceElement, _apidomNsOpenapi.isJSONReferenceLikeElement, _apidomNsOpenapi.refractJSONReference, 'JSON Reference Object');
469
+ }
470
+
471
+ /**
472
+ * Path Item Object is not a top-level definition field in OpenAPI 2.0, so an
473
+ * external Path Item reference cannot be hoisted; it is inlined in place
474
+ * instead, after bundling its own external references.
475
+ */
476
+ async PathItemElement(path) {
477
+ const pathItemElement = path.node;
478
+
479
+ // ignore Path Item Objects without a $ref field
480
+ if (!(0, _apidomDatamodel.isStringElement)(pathItemElement.$ref)) {
481
+ return;
482
+ }
483
+ const $ref = (0, _apidomCore.toValue)(pathItemElement.$ref);
484
+ const retrievalURI = this.toBaseURI($ref);
485
+ const isInternalReference = this.entryURI === retrievalURI;
486
+ const isExternalReference = !isInternalReference;
487
+ if (isInternalReference) {
488
+ // normalize self-file references to a bare fragment so the bundled
489
+ // document stays transferable; bare fragments are left untouched
490
+ pathItemElement.set('$ref', this.normalizeSelfFileRef($ref));
491
+ return;
492
+ }
493
+
494
+ // honor the resolve.external switch
495
+ if (!this.options.resolve.external && isExternalReference) {
496
+ return;
497
+ }
498
+ const $refBaseURI = url.resolve(retrievalURI, $ref);
499
+ const jsonPointer = _apidomJsonPointer.URIFragmentIdentifier.fromURIReference($refBaseURI);
500
+ const canonicalKey = `${retrievalURI}#${jsonPointer}`;
501
+
502
+ // a circular external Path Item reference cannot be inlined without
503
+ // recursing forever; leave the $ref in place to break the cycle
504
+ if (this.inlineStack.has(canonicalKey)) {
505
+ return;
506
+ }
507
+ try {
508
+ // detect maximum depth of bundling
509
+ if (this.reference.depth >= this.options.bundle.maxDepth) {
510
+ throw new _MaximumBundleDepthError.default(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
511
+ maxDepth: this.options.bundle.maxDepth,
512
+ uri: this.reference.uri
513
+ });
514
+ }
515
+ const reference = await this.toReference($ref);
516
+ let referencedElement = (0, _apidomJsonPointer.evaluate)(reference.value.result, jsonPointer);
517
+
518
+ // apply Path Item semantics to the referenced fragment
519
+ if (!(0, _apidomNsOpenapi.isPathItemElement)(referencedElement)) {
520
+ const cached = this.getRefracted(referencedElement, 'pathItem');
521
+ if (cached !== undefined) {
522
+ referencedElement = cached;
523
+ } else {
524
+ const sourceElement = referencedElement;
525
+ referencedElement = (0, _apidomNsOpenapi.refractPathItem)(referencedElement);
526
+ this.setRefracted(sourceElement, 'pathItem', referencedElement);
527
+ }
528
+ }
529
+
530
+ // own a copy and bundle the external references it contains
531
+ const inlinedElement = (0, _apidomDatamodel.cloneDeep)(referencedElement);
532
+ this.inlineStack.add(canonicalKey);
533
+ const visitor = new OpenAPI2BundleVisitor({
534
+ reference,
535
+ options: this.options,
536
+ assignments: this.assignments,
537
+ reservedNames: this.reservedNames,
538
+ refractCache: this.refractCache,
539
+ inlineStack: this.inlineStack
540
+ });
541
+ let bundledElement;
542
+ try {
543
+ bundledElement = await (0, _apidomTraverse.traverseAsync)(inlinedElement, visitor, {
544
+ mutable: true
545
+ });
546
+ } finally {
547
+ this.inlineStack.delete(canonicalKey);
548
+ }
549
+
550
+ // merge sibling fields from the referencing Path Item over the referenced
551
+ // one. The referencing `$ref` is being resolved away and is skipped; the
552
+ // referenced element keeps its own `$ref` if one survived (i.e. it was
553
+ // left in place to break a circular external Path Item chain).
554
+ pathItemElement.forEach((_value, keyElement, item) => {
555
+ const key = (0, _apidomCore.toValue)(keyElement);
556
+ if (key === '$ref') return;
557
+ bundledElement.remove(key);
558
+ bundledElement.content.push(item);
559
+ });
560
+ bundledElement.meta.set('ref-origin', reference.uri);
561
+ path.replaceWith(bundledElement);
562
+ } catch (error) {
563
+ this.handleError(`Error while bundling Path Item Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, pathItemElement, '$ref', $ref, path);
564
+ }
565
+ }
566
+ }
567
+ var _default = exports.default = OpenAPI2BundleVisitor;