@speclynx/apidom-reference 4.14.0 → 4.15.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,409 @@
1
+ import { none } from 'ramda';
2
+ import { isElement, isObjectElement, isStringElement, AnnotationElement, cloneDeep } from '@speclynx/apidom-datamodel';
3
+ import { toValue, toYAML, fixedFields } from '@speclynx/apidom-core';
4
+ import { traverseAsync } from '@speclynx/apidom-traverse';
5
+ import { evaluate as jsonPointerEvaluate, escape, URIFragmentIdentifier } from '@speclynx/apidom-json-pointer';
6
+ import { ComponentsElement, ComponentsInputsElement } from '@speclynx/apidom-ns-arazzo-1';
7
+ import BundleError from "../../../errors/BundleError.mjs";
8
+ import UnresolvableReferenceError from "../../../errors/UnresolvableReferenceError.mjs";
9
+ import MaximumBundleDepthError from "../../../errors/MaximumBundleDepthError.mjs";
10
+ import MaximumResolveDepthError from "../../../errors/MaximumResolveDepthError.mjs";
11
+ import EvaluationJsonSchemaUriError from "../../../errors/EvaluationJsonSchemaUriError.mjs";
12
+ import * as url from "../../../util/url.mjs";
13
+ import parse from "../../../parse/index.mjs";
14
+ import Reference from "../../../Reference.mjs";
15
+ import File from "../../../File.mjs";
16
+ import { isAnchor, uriToAnchor, evaluate as $anchorEvaluate } from "../../../dereference/strategies/arazzo-1/selectors/$anchor.mjs";
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";
19
+ import { toPascalCase, sanitizeComponentName, uniqueName as resolveUniqueName } from "../../util.mjs";
20
+ /**
21
+ * The Components Object field where embedded JSON Schema resources are hoisted.
22
+ * Sourced from the Components Object fixed fields so it cannot drift from the
23
+ * namespace definition.
24
+ */
25
+ const cf = fixedFields(ComponentsElement, {
26
+ indexed: true
27
+ });
28
+
29
+ /**
30
+ * @public
31
+ */
32
+
33
+ /**
34
+ * @public
35
+ */
36
+ class Arazzo1BundleVisitor {
37
+ reference;
38
+ options;
39
+
40
+ /**
41
+ * The entry parse result. Derived from the shared refSet's root reference.
42
+ */
43
+ get entryParseResult() {
44
+ return this.reference.refSet.rootRef.value;
45
+ }
46
+
47
+ /**
48
+ * The entry document element. Embedded external schema resources are placed
49
+ * into its `components.inputs` object. The `canBundle` guard guarantees it is
50
+ * an ArazzoSpecification1Element.
51
+ */
52
+ get entryResult() {
53
+ return this.entryParseResult.result;
54
+ }
55
+
56
+ /**
57
+ * Shared across the entry document and every external document visitor.
58
+ * Guarantees each external resource is embedded exactly once, keyed by its
59
+ * resource URI.
60
+ */
61
+ assignments;
62
+
63
+ /**
64
+ * Component names already reserved per Components Object field. Reserved
65
+ * before recursion so collision suffixing (`Pet`, `Pet-2`, ...) accounts for
66
+ * components that are still being bundled.
67
+ */
68
+ reservedNames;
69
+ constructor({
70
+ reference,
71
+ options,
72
+ assignments = new Map(),
73
+ reservedNames = new Map()
74
+ }) {
75
+ this.reference = reference;
76
+ this.options = options;
77
+ this.assignments = assignments;
78
+ this.reservedNames = reservedNames;
79
+ }
80
+ toBaseURI(uri) {
81
+ return url.resolve(this.reference.uri, url.sanitize(url.stripHash(uri)));
82
+ }
83
+ async toReference(uri) {
84
+ // detect maximum depth of resolution
85
+ if (this.reference.depth >= this.options.resolve.maxDepth) {
86
+ throw new MaximumResolveDepthError(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`, {
87
+ maxDepth: this.options.resolve.maxDepth,
88
+ uri: this.reference.uri
89
+ });
90
+ }
91
+ const baseURI = this.toBaseURI(uri);
92
+ const {
93
+ refSet
94
+ } = this.reference;
95
+
96
+ // we've already processed this Reference in past
97
+ if (refSet.has(baseURI)) {
98
+ return refSet.find(ref => ref.uri === baseURI);
99
+ }
100
+ const parseResult = await parse(url.unsanitize(baseURI), {
101
+ ...this.options,
102
+ parse: {
103
+ ...this.options.parse,
104
+ mediaType: 'text/plain'
105
+ }
106
+ });
107
+ const reference = new Reference({
108
+ uri: baseURI,
109
+ value: parseResult,
110
+ depth: this.reference.depth + 1
111
+ });
112
+ refSet.add(reference);
113
+ return reference;
114
+ }
115
+
116
+ /**
117
+ * Handles an error according to the `bundle.continueOnError` option.
118
+ *
119
+ * For new errors: wraps in UnresolvableReferenceError with structured
120
+ * context. For errors already wrapped by a nested visitor: prepends the
121
+ * current hop to the trace.
122
+ *
123
+ * Inner/intermediate visitors always throw to let the trace accumulate. Only
124
+ * the entry document visitor respects continueOnError (callback/swallow/throw).
125
+ */
126
+ handleError(message, error, referencingElement, refFieldName, refFieldValue, visitorPath) {
127
+ // a collision reported as `error` is a deliberate hard stop with no
128
+ // resolution-failure analog, so pass it through unchanged. A maxDepth
129
+ // breach (MaximumBundleDepthError, which also extends BundleError) is a
130
+ // resolution failure and flows through below — wrapped and subject to
131
+ // continueOnError, like dereferencing.
132
+ if (error instanceof BundleError && !(error instanceof MaximumBundleDepthError)) {
133
+ throw error;
134
+ }
135
+ const {
136
+ continueOnError
137
+ } = this.options.bundle;
138
+ const isEntryDocument = url.stripHash(this.reference.refSet?.rootRef?.uri ?? '') === this.reference.uri;
139
+ const uri = this.reference.uri;
140
+ const type = referencingElement.element;
141
+ const codeFrame = toYAML(referencingElement);
142
+ const location = visitorPath.formatPath();
143
+ const hop = {
144
+ uri,
145
+ type,
146
+ refFieldName,
147
+ refFieldValue,
148
+ location,
149
+ codeFrame
150
+ };
151
+ let unresolvedError;
152
+ if (error instanceof UnresolvableReferenceError) {
153
+ // @ts-ignore
154
+ error.trace = [hop, ...error.trace];
155
+ unresolvedError = error;
156
+ } else {
157
+ unresolvedError = new UnresolvableReferenceError(message, {
158
+ cause: error,
159
+ type,
160
+ uri,
161
+ location,
162
+ codeFrame,
163
+ refFieldName,
164
+ refFieldValue,
165
+ trace: []
166
+ });
167
+ }
168
+ if (!isEntryDocument || continueOnError === false) throw unresolvedError;
169
+ if (typeof continueOnError === 'function') continueOnError(unresolvedError);
170
+ }
171
+
172
+ /**
173
+ * Lazily creates the Components Object (and the `inputs` field within it) on
174
+ * the entry document, returning the field where a resource is embedded.
175
+ */
176
+ ensureInputsField() {
177
+ const entryResult = this.entryResult;
178
+ let components = entryResult.components;
179
+ if (!isObjectElement(components)) {
180
+ components = new ComponentsElement();
181
+ entryResult.components = components;
182
+ }
183
+ let inputs = components.get(cf.inputs.name);
184
+ if (!isObjectElement(inputs)) {
185
+ inputs = new ComponentsInputsElement();
186
+ components.set(cf.inputs.name, inputs);
187
+ }
188
+ return inputs;
189
+ }
190
+
191
+ /**
192
+ * Computes the base component name (before collision suffixing) according to
193
+ * the configured naming strategy.
194
+ */
195
+ baseName(element, baseURI) {
196
+ // strategy specific options take precedence over the top-level bundle options
197
+ const componentNamesStrategy = this.options.bundle.strategyOpts['arazzo-1']?.componentNamesStrategy ?? this.options.bundle.componentNamesStrategy;
198
+ const fallback = url.getBasename(baseURI) || 'Schema';
199
+ if (typeof componentNamesStrategy === 'function') {
200
+ const resolved = componentNamesStrategy({
201
+ element,
202
+ field: cf.inputs.name,
203
+ jsonPointer: '',
204
+ baseURI
205
+ });
206
+ if (typeof resolved === 'string' && resolved !== '') {
207
+ return resolved;
208
+ }
209
+ return fallback;
210
+ }
211
+ if (componentNamesStrategy === 'title') {
212
+ const title = isObjectElement(element) ? toValue(element.get('title')) : undefined;
213
+ if (typeof title === 'string' && title.trim() !== '') {
214
+ const name = sanitizeComponentName(toPascalCase(title));
215
+ if (name !== '') return name;
216
+ }
217
+ // fall back to basename when no usable title is present
218
+ }
219
+ return fallback;
220
+ }
221
+
222
+ /**
223
+ * Computes a collision-free component name within `components.inputs`. A name
224
+ * is taken if it's already placed in inputs or reserved by a component that is
225
+ * still being bundled (reserved before recursion).
226
+ */
227
+ uniqueName(candidate) {
228
+ const field = cf.inputs.name;
229
+ const inputs = this.ensureInputsField();
230
+ const reserved = this.reservedNames.get(field) ?? new Set();
231
+ return resolveUniqueName(candidate, name => inputs.hasKey(name) || reserved.has(name));
232
+ }
233
+
234
+ /**
235
+ * Reports a collision-forced rename according to the configured severity.
236
+ * A rename means two distinct targets resolved to the same name; each keeps
237
+ * its own component (and origin), so report the rename per severity.
238
+ */
239
+ reportNameCollision(preferredName, componentName) {
240
+ // strategy specific options take precedence over the top-level bundle options
241
+ const onComponentNameCollision = this.options.bundle.strategyOpts['arazzo-1']?.onComponentNameCollision ?? this.options.bundle.onComponentNameCollision;
242
+ if (componentName === preferredName || onComponentNameCollision === 'off') {
243
+ return;
244
+ }
245
+ const message = `Component "${preferredName}" in components/${cf.inputs.name} is referenced with the same name but different content. Renamed to "${componentName}".`;
246
+ if (onComponentNameCollision === 'error') {
247
+ throw new BundleError(message);
248
+ }
249
+ const annotation = new AnnotationElement(message);
250
+ annotation.classes.push('warning');
251
+ annotation.code = 'bundle-component-name-collision';
252
+ // append (never prepend) so the result element stays ahead of annotations
253
+ this.entryParseResult.content.push(annotation);
254
+ }
255
+
256
+ /**
257
+ * JSON Schema Objects in Arazzo (used by Input Objects and inline schemas) are
258
+ * a JSON Schema 2020-12 dialect — the only external reference type Arazzo
259
+ * defines. They are bundled per the JSON Schema Compound Document rules: the
260
+ * external schema RESOURCE is embedded verbatim into `components.inputs`
261
+ * (carrying its `$id`), and the referencing `$ref` is left UNCHANGED — it
262
+ * keeps resolving against the embedded resource's `$id`. This mirrors the
263
+ * openapi-3-1 strategy's Schema Object handling.
264
+ */
265
+ async JSONSchemaElement(path) {
266
+ const referencingElement = path.node;
267
+
268
+ // skip current referencing schema as $ref keyword was not defined
269
+ if (!isStringElement(referencingElement.$ref)) {
270
+ return;
271
+ }
272
+ const $ref = toValue(referencingElement.$ref);
273
+ 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
277
+ // resolve.maxDepth guard.
278
+ const retrievalURI = this.reference.uri;
279
+ const $refBaseURI = resolveSchema$refField(retrievalURI, referencingElement);
280
+ const $refBaseURIStrippedHash = url.stripHash($refBaseURI);
281
+ const file = new File({
282
+ uri: $refBaseURIStrippedHash
283
+ });
284
+ const isUnknownURI = none(r => r.canRead(file), this.options.resolve.resolvers);
285
+ const isURL = !isUnknownURI;
286
+ const isInternalReference = url.stripHash(this.reference.uri) === $refBaseURIStrippedHash;
287
+ const isExternalReference = !isInternalReference;
288
+
289
+ // internal Schema Object $refs resolve against the in-document $id graph;
290
+ // leaving them untouched keeps that resolution valid. A JSON Schema $ref is
291
+ // never normalized to a bare fragment.
292
+ if (isInternalReference) {
293
+ return;
294
+ }
295
+
296
+ // honor the resolve.external switch
297
+ if (!this.options.resolve.external && isExternalReference) {
298
+ return;
299
+ }
300
+
301
+ // detect maximum depth of bundling before fetching the external resource
302
+ if (this.reference.depth >= this.options.bundle.maxDepth) {
303
+ throw new MaximumBundleDepthError(`Maximum bundle depth of "${this.options.bundle.maxDepth}" has been exceeded in file "${this.reference.uri}"`, {
304
+ maxDepth: this.options.bundle.maxDepth,
305
+ uri: this.reference.uri
306
+ });
307
+ }
308
+
309
+ // locate the external schema resource the $ref targets, reusing the
310
+ // dereference strategy's classification. First try to resolve the target
311
+ // as a canonical URI / URL against the current document's $id graph; if
312
+ // that fails, fall back to fetching the external document and resolving
313
+ // the fragment there as a $anchor or JSON Pointer.
314
+ let schemaReference = this.reference;
315
+ try {
316
+ const referenceAsSchema = maybeRefractToJSONSchemaElement(schemaReference.value.result);
317
+ uriEvaluate($refBaseURI, referenceAsSchema);
318
+ } catch (error) {
319
+ if (isURL && error instanceof EvaluationJsonSchemaUriError) {
320
+ if (isAnchor(uriToAnchor($refBaseURI))) {
321
+ schemaReference = await this.toReference(url.unsanitize($refBaseURI));
322
+ const selector = uriToAnchor($refBaseURI);
323
+ const referenceAsSchema = maybeRefractToJSONSchemaElement(schemaReference.value.result);
324
+ $anchorEvaluate(selector, referenceAsSchema);
325
+ } else {
326
+ schemaReference = await this.toReference(url.unsanitize($refBaseURI));
327
+ const selector = URIFragmentIdentifier.fromURIReference($refBaseURI);
328
+ const referenceAsSchema = maybeRefractToJSONSchemaElement(schemaReference.value.result);
329
+ jsonPointerEvaluate(referenceAsSchema, selector);
330
+ }
331
+ } else {
332
+ throw error;
333
+ }
334
+ }
335
+
336
+ // a $ref that resolves (by $id/$anchor/pointer) within the current document
337
+ // is internal even when its URI form (e.g. a URN or absolute $id) is not a
338
+ // bare fragment. Leave it untouched — it resolves against the in-document
339
+ // $id graph, and embedding the current document into itself would be wrong.
340
+ if (url.stripHash(schemaReference.uri) === url.stripHash(this.reference.uri)) {
341
+ return;
342
+ }
343
+
344
+ // the resource to embed is the WHOLE external document (its root schema),
345
+ // identified by its $id. A $ref into a fragment of the document embeds the
346
+ // entire document resource once; the fragment keeps resolving against the
347
+ // embedded $id.
348
+ const resourceRoot = maybeRefractToJSONSchemaElement(schemaReference.value.result);
349
+ const resourceBaseURI = resolveSchema$idField(url.stripHash(schemaReference.uri), resourceRoot) ?? url.stripHash(schemaReference.uri);
350
+ const field = cf.inputs.name;
351
+ const canonicalKey = `${field}\t${resourceBaseURI}`;
352
+
353
+ // resource already embedded (or being embedded) — leave the referencing
354
+ // $ref untouched and stop. This also terminates circular external schema
355
+ // references, since the assignment is reserved BEFORE recursion.
356
+ if (this.assignments.has(canonicalKey)) {
357
+ path.skip();
358
+ return;
359
+ }
360
+
361
+ // own a copy of the resource and ensure it carries a $id so the unchanged
362
+ // $ref still resolves against it once embedded
363
+ const embeddedElement = cloneDeep(resourceRoot);
364
+ if (!isStringElement(embeddedElement.$id)) {
365
+ embeddedElement.set('$id', resourceBaseURI);
366
+ }
367
+
368
+ // the embedded thing is the WHOLE resource (keyed by its $id), so its name
369
+ // is derived from the resource URI
370
+ const preferredName = this.baseName(embeddedElement, resourceBaseURI);
371
+ const componentName = this.uniqueName(preferredName);
372
+ this.reportNameCollision(preferredName, componentName);
373
+
374
+ // reserve the assignment and component name before recursing so circular
375
+ // external schema references terminate
376
+ this.assignments.set(canonicalKey, `#/components/${field}/${escape(componentName)}`);
377
+ if (!this.reservedNames.has(field)) this.reservedNames.set(field, new Set());
378
+ this.reservedNames.get(field).add(componentName);
379
+
380
+ // bundle external references contained within the embedded resource;
381
+ // nested external resources land flat in the same components.inputs
382
+ const visitor = new Arazzo1BundleVisitor({
383
+ reference: schemaReference,
384
+ options: this.options,
385
+ assignments: this.assignments,
386
+ reservedNames: this.reservedNames
387
+ });
388
+ const bundledElement = await traverseAsync(embeddedElement, visitor, {
389
+ mutable: true
390
+ });
391
+
392
+ // annotate the embedded resource with info about its origin
393
+ if (isElement(bundledElement)) {
394
+ bundledElement.meta.set('ref-origin', schemaReference.uri);
395
+ }
396
+
397
+ // place the embedded resource into the entry document's components.inputs
398
+ this.ensureInputsField().set(componentName, bundledElement);
399
+
400
+ // the referencing $ref is left UNCHANGED — it resolves against the
401
+ // embedded resource's $id
402
+ path.skip();
403
+ } catch (error) {
404
+ this.handleError(`Error while bundling JSON Schema Object. Cannot resolve $ref "${$ref}": ${error.message}`, error, referencingElement, '$ref', $ref, path);
405
+ path.skip();
406
+ }
407
+ }
408
+ }
409
+ export default Arazzo1BundleVisitor;
@@ -39,18 +39,19 @@ var _index28 = _interopRequireDefault(require("../dereference/strategies/overlay
39
39
  var _index29 = _interopRequireDefault(require("../bundle/strategies/openapi-2/index.cjs"));
40
40
  var _index30 = _interopRequireDefault(require("../bundle/strategies/openapi-3-0/index.cjs"));
41
41
  var _index31 = _interopRequireDefault(require("../bundle/strategies/openapi-3-1/index.cjs"));
42
- var _index32 = require("../index.cjs");
43
- Object.keys(_index32).forEach(function (key) {
42
+ var _index32 = _interopRequireDefault(require("../bundle/strategies/arazzo-1/index.cjs"));
43
+ var _index33 = require("../index.cjs");
44
+ Object.keys(_index33).forEach(function (key) {
44
45
  if (key === "default" || key === "__esModule") return;
45
- if (key in exports && exports[key] === _index32[key]) return;
46
+ if (key in exports && exports[key] === _index33[key]) return;
46
47
  Object.defineProperty(exports, key, {
47
48
  enumerable: true,
48
49
  get: function () {
49
- return _index32[key];
50
+ return _index33[key];
50
51
  }
51
52
  });
52
53
  });
53
- _index32.options.parse.parsers = [new _index9.default({
54
+ _index33.options.parse.parsers = [new _index9.default({
54
55
  allowEmpty: true,
55
56
  sourceMap: false
56
57
  }), new _index0.default({
@@ -98,11 +99,11 @@ _index32.options.parse.parsers = [new _index9.default({
98
99
  }), new _indexNode2.default({
99
100
  allowEmpty: true
100
101
  })];
101
- _index32.options.resolve.resolvers = [new _indexNode.default(), new _index.default({
102
+ _index33.options.resolve.resolvers = [new _indexNode.default(), new _index.default({
102
103
  timeout: 5000,
103
104
  redirects: 5,
104
105
  withCredentials: false
105
106
  })];
106
- _index32.options.resolve.strategies = [new _index2.default(), new _index3.default(), new _index4.default(), new _index5.default(), new _index6.default(), new _index7.default(), new _index8.default()];
107
- _index32.options.dereference.strategies = [new _index23.default(), new _index24.default(), new _index25.default(), new _index26.default(), new _index27.default(), new _index28.default(), new _index22.default()];
108
- _index32.options.bundle.strategies = [new _index29.default(), new _index30.default(), new _index31.default()];
107
+ _index33.options.resolve.strategies = [new _index2.default(), new _index3.default(), new _index4.default(), new _index5.default(), new _index6.default(), new _index7.default(), new _index8.default()];
108
+ _index33.options.dereference.strategies = [new _index23.default(), new _index24.default(), new _index25.default(), new _index26.default(), new _index27.default(), new _index28.default(), new _index22.default()];
109
+ _index33.options.bundle.strategies = [new _index29.default(), new _index30.default(), new _index31.default(), new _index32.default()];
@@ -33,6 +33,7 @@ import Overlay1DereferenceStrategy from "../dereference/strategies/overlay-1/ind
33
33
  import OpenAPI2BundleStrategy from "../bundle/strategies/openapi-2/index.mjs";
34
34
  import OpenAPI3_0BundleStrategy from "../bundle/strategies/openapi-3-0/index.mjs";
35
35
  import OpenAPI3_1BundleStrategy from "../bundle/strategies/openapi-3-1/index.mjs";
36
+ import Arazzo1BundleStrategy from "../bundle/strategies/arazzo-1/index.mjs";
36
37
  import { options } from "../index.mjs";
37
38
  options.parse.parsers = [new OpenAPIJSON2Parser({
38
39
  allowEmpty: true,
@@ -89,5 +90,5 @@ options.resolve.resolvers = [new FileResolver(), new HTTPResolverAxios({
89
90
  })];
90
91
  options.resolve.strategies = [new OpenAPI2ResolveStrategy(), new OpenAPI3_0ResolveStrategy(), new OpenAPI3_1ResolveStrategy(), new AsyncAPI2ResolveStrategy(), new Arazzo1ResolveStrategy(), new Overlay1ResolveStrategy(), new ApiDOMResolveStrategy()];
91
92
  options.dereference.strategies = [new OpenAPI2DereferenceStrategy(), new OpenAPI3_0DereferenceStrategy(), new OpenAPI3_1DereferenceStrategy(), new AsyncAPI2DereferenceStrategy(), new Arazzo1DereferenceStrategy(), new Overlay1DereferenceStrategy(), new ApiDOMDereferenceStrategy()];
92
- options.bundle.strategies = [new OpenAPI2BundleStrategy(), new OpenAPI3_0BundleStrategy(), new OpenAPI3_1BundleStrategy()];
93
+ options.bundle.strategies = [new OpenAPI2BundleStrategy(), new OpenAPI3_0BundleStrategy(), new OpenAPI3_1BundleStrategy(), new Arazzo1BundleStrategy()];
93
94
  export * from "../index.mjs";
@@ -0,0 +1,31 @@
1
+ import { ParseResultElement } from '@speclynx/apidom-datamodel';
2
+ import File from '../../../File.ts';
3
+ import BundleStrategy, { BundleStrategyOptions } from '../BundleStrategy.ts';
4
+ import Arazzo1BundleVisitor from './visitor.ts';
5
+ import type { ReferenceOptions } from '../../../options/index.ts';
6
+ export type { default as DereferenceStrategy, DereferenceStrategyOptions, } from '../../../dereference/strategies/DereferenceStrategy.ts';
7
+ export type { default as File, FileOptions } from '../../../File.ts';
8
+ export type { default as Reference, ReferenceOptions } from '../../../Reference.ts';
9
+ export type { default as ReferenceSet, ReferenceSetOptions } from '../../../ReferenceSet.ts';
10
+ export type { Arazzo1BundleVisitorOptions } from './visitor.ts';
11
+ export type { ReferenceOptions as ApiDOMReferenceOptions, ReferenceBundleOptions as ApiDOMReferenceBundleOptions, ReferenceDereferenceOptions as ApiDOMReferenceDereferenceOptions, ReferenceParseOptions as ApiDOMReferenceParseOptions, ReferenceResolveOptions as ApiDOMReferenceResolveOptions, } from '../../../options/index.ts';
12
+ export type { default as Parser, ParserOptions } from '../../../parse/parsers/Parser.ts';
13
+ export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
14
+ export type { default as ResolveStrategy, ResolveStrategyOptions, } from '../../../resolve/strategies/ResolveStrategy.ts';
15
+ export type { default as BundleStrategy, BundleStrategyOptions } from '../BundleStrategy.ts';
16
+ export type { ComponentNamesStrategy, ComponentNameResolver, ComponentNameResolverArgs, ComponentNameCollisionSeverity, } from '../../../options/index.ts';
17
+ /**
18
+ * @public
19
+ */
20
+ export interface Arazzo1BundleStrategyOptions extends Omit<BundleStrategyOptions, 'name'> {
21
+ }
22
+ /**
23
+ * @public
24
+ */
25
+ declare class Arazzo1BundleStrategy extends BundleStrategy {
26
+ constructor(options?: Arazzo1BundleStrategyOptions);
27
+ canBundle(file: File): boolean;
28
+ bundle(file: File, options: ReferenceOptions): Promise<ParseResultElement>;
29
+ }
30
+ export { Arazzo1BundleVisitor };
31
+ export default Arazzo1BundleStrategy;
@@ -0,0 +1,90 @@
1
+ import { Element, ObjectElement, ParseResultElement } from '@speclynx/apidom-datamodel';
2
+ import { type Path } from '@speclynx/apidom-traverse';
3
+ import { ArazzoSpecification1Element } from '@speclynx/apidom-ns-arazzo-1';
4
+ import Reference from '../../../Reference.ts';
5
+ import type { ReferenceOptions } from '../../../options/index.ts';
6
+ /**
7
+ * @public
8
+ */
9
+ export interface Arazzo1BundleVisitorOptions {
10
+ readonly reference: Reference;
11
+ readonly options: ReferenceOptions;
12
+ readonly assignments?: Map<string, string>;
13
+ readonly reservedNames?: Map<string, Set<string>>;
14
+ }
15
+ /**
16
+ * @public
17
+ */
18
+ declare class Arazzo1BundleVisitor {
19
+ protected readonly reference: Reference;
20
+ protected readonly options: ReferenceOptions;
21
+ /**
22
+ * The entry parse result. Derived from the shared refSet's root reference.
23
+ */
24
+ protected get entryParseResult(): ParseResultElement;
25
+ /**
26
+ * The entry document element. Embedded external schema resources are placed
27
+ * into its `components.inputs` object. The `canBundle` guard guarantees it is
28
+ * an ArazzoSpecification1Element.
29
+ */
30
+ protected get entryResult(): ArazzoSpecification1Element;
31
+ /**
32
+ * Shared across the entry document and every external document visitor.
33
+ * Guarantees each external resource is embedded exactly once, keyed by its
34
+ * resource URI.
35
+ */
36
+ protected readonly assignments: Map<string, string>;
37
+ /**
38
+ * Component names already reserved per Components Object field. Reserved
39
+ * before recursion so collision suffixing (`Pet`, `Pet-2`, ...) accounts for
40
+ * components that are still being bundled.
41
+ */
42
+ protected readonly reservedNames: Map<string, Set<string>>;
43
+ constructor({ reference, options, assignments, reservedNames, }: Arazzo1BundleVisitorOptions);
44
+ protected toBaseURI(uri: string): string;
45
+ protected toReference(uri: string): Promise<Reference>;
46
+ /**
47
+ * Handles an error according to the `bundle.continueOnError` option.
48
+ *
49
+ * For new errors: wraps in UnresolvableReferenceError with structured
50
+ * context. For errors already wrapped by a nested visitor: prepends the
51
+ * current hop to the trace.
52
+ *
53
+ * Inner/intermediate visitors always throw to let the trace accumulate. Only
54
+ * the entry document visitor respects continueOnError (callback/swallow/throw).
55
+ */
56
+ protected handleError(message: string, error: Error, referencingElement: Element, refFieldName: string, refFieldValue: string, visitorPath: Path<Element>): void;
57
+ /**
58
+ * Lazily creates the Components Object (and the `inputs` field within it) on
59
+ * the entry document, returning the field where a resource is embedded.
60
+ */
61
+ protected ensureInputsField(): ObjectElement;
62
+ /**
63
+ * Computes the base component name (before collision suffixing) according to
64
+ * the configured naming strategy.
65
+ */
66
+ protected baseName(element: Element, baseURI: string): string;
67
+ /**
68
+ * Computes a collision-free component name within `components.inputs`. A name
69
+ * is taken if it's already placed in inputs or reserved by a component that is
70
+ * still being bundled (reserved before recursion).
71
+ */
72
+ protected uniqueName(candidate: string): string;
73
+ /**
74
+ * Reports a collision-forced rename according to the configured severity.
75
+ * A rename means two distinct targets resolved to the same name; each keeps
76
+ * its own component (and origin), so report the rename per severity.
77
+ */
78
+ protected reportNameCollision(preferredName: string, componentName: string): void;
79
+ /**
80
+ * JSON Schema Objects in Arazzo (used by Input Objects and inline schemas) are
81
+ * a JSON Schema 2020-12 dialect — the only external reference type Arazzo
82
+ * defines. They are bundled per the JSON Schema Compound Document rules: the
83
+ * external schema RESOURCE is embedded verbatim into `components.inputs`
84
+ * (carrying its `$id`), and the referencing `$ref` is left UNCHANGED — it
85
+ * keeps resolving against the embedded resource's `$id`. This mirrors the
86
+ * openapi-3-1 strategy's Schema Object handling.
87
+ */
88
+ JSONSchemaElement(path: Path<Element>): Promise<void>;
89
+ }
90
+ export default Arazzo1BundleVisitor;