@speclynx/apidom-reference 5.2.4 → 5.2.6

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 (53) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +37 -14
  3. package/dist/apidom-reference.browser.js +1234 -596
  4. package/dist/apidom-reference.browser.min.js +1 -1
  5. package/package.json +29 -29
  6. package/src/bundle/strategies/arazzo-1/visitor.cjs +105 -22
  7. package/src/bundle/strategies/arazzo-1/visitor.mjs +107 -24
  8. package/src/bundle/strategies/openapi-3-1/visitor.cjs +197 -47
  9. package/src/bundle/strategies/openapi-3-1/visitor.mjs +200 -50
  10. package/src/bundle/util.cjs +57 -2
  11. package/src/bundle/util.mjs +52 -0
  12. package/src/dereference/strategies/arazzo-1/index.cjs +20 -17
  13. package/src/dereference/strategies/arazzo-1/index.mjs +14 -11
  14. package/src/dereference/strategies/arazzo-1/selectors/$anchor.cjs +6 -0
  15. package/src/dereference/strategies/arazzo-1/selectors/$anchor.mjs +1 -1
  16. package/src/dereference/strategies/arazzo-1/selectors/uri.cjs +6 -0
  17. package/src/dereference/strategies/arazzo-1/selectors/uri.mjs +1 -1
  18. package/src/dereference/strategies/arazzo-1/source-descriptions.cjs +65 -42
  19. package/src/dereference/strategies/arazzo-1/source-descriptions.mjs +64 -42
  20. package/src/dereference/strategies/arazzo-1/util.cjs +6 -0
  21. package/src/dereference/strategies/arazzo-1/util.mjs +1 -1
  22. package/src/dereference/strategies/arazzo-1/visitor.cjs +97 -35
  23. package/src/dereference/strategies/arazzo-1/visitor.mjs +101 -39
  24. package/src/dereference/strategies/openapi-3-1/index.cjs +2 -1
  25. package/src/dereference/strategies/openapi-3-1/index.mjs +3 -1
  26. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.cjs +16 -4
  27. package/src/dereference/strategies/openapi-3-1/selectors/$anchor.mjs +14 -3
  28. package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.cjs +29 -0
  29. package/src/dereference/strategies/openapi-3-1/selectors/json-pointer.mjs +22 -0
  30. package/src/dereference/strategies/openapi-3-1/selectors/uri.cjs +54 -27
  31. package/src/dereference/strategies/openapi-3-1/selectors/uri.mjs +51 -27
  32. package/src/dereference/strategies/openapi-3-1/util.cjs +75 -14
  33. package/src/dereference/strategies/openapi-3-1/util.mjs +68 -14
  34. package/src/dereference/strategies/openapi-3-1/visitor.cjs +85 -9
  35. package/src/dereference/strategies/openapi-3-1/visitor.mjs +89 -13
  36. package/src/util/url.cjs +35 -2
  37. package/src/util/url.mjs +32 -0
  38. package/types/apidom-reference.d.ts +12 -0
  39. package/types/bundle/strategies/arazzo-1/index.d.ts +1 -1
  40. package/types/bundle/strategies/arazzo-1/visitor.d.ts +51 -6
  41. package/types/bundle/strategies/openapi-3-1/index.d.ts +1 -1
  42. package/types/bundle/strategies/openapi-3-1/visitor.d.ts +59 -3
  43. package/types/bundle/util.d.ts +39 -0
  44. package/types/dereference/strategies/arazzo-1/selectors/$anchor.d.ts +1 -1
  45. package/types/dereference/strategies/arazzo-1/selectors/uri.d.ts +1 -1
  46. package/types/dereference/strategies/arazzo-1/util.d.ts +1 -1
  47. package/types/dereference/strategies/arazzo-1/visitor.d.ts +17 -1
  48. package/types/dereference/strategies/openapi-3-1/selectors/$anchor.d.ts +8 -0
  49. package/types/dereference/strategies/openapi-3-1/selectors/json-pointer.d.ts +9 -0
  50. package/types/dereference/strategies/openapi-3-1/selectors/uri.d.ts +37 -4
  51. package/types/dereference/strategies/openapi-3-1/util.d.ts +53 -2
  52. package/types/dereference/strategies/openapi-3-1/visitor.d.ts +17 -1
  53. package/types/util/url.d.ts +10 -0
package/src/util/url.cjs CHANGED
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequ
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
7
+ exports.unsanitize = exports.toFileSystemPath = exports.stripHash = exports.sanitize = exports.resolve = exports.relative = exports.isURI = exports.isHttpUrl = exports.isFileSystemPath = exports.hasProtocol = exports.getProtocol = exports.getHash = exports.getExtension = exports.getBasename = exports.fromFileSystemPath = exports.cwd = void 0;
8
8
  var _process = _interopRequireDefault(require("process"));
9
9
  var _ramda = require("ramda");
10
10
  var _ramdaAdjunct = require("ramda-adjunct");
@@ -275,6 +275,39 @@ const resolve = (from, to) => {
275
275
  return resolvedUrl.toString();
276
276
  };
277
277
 
278
+ /**
279
+ * Computes the URI reference that resolves to `to` against `from` — the inverse
280
+ * of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
281
+ * relative to the directory of `from` (its last path segment is the document,
282
+ * not a directory). When the two URIs differ in scheme or authority, or either
283
+ * has no hierarchical path (e.g. a URN), no relative form exists and `to` is
284
+ * returned unchanged.
285
+ * @public
286
+ */
287
+ exports.resolve = resolve;
288
+ const relative = (from, to) => {
289
+ const fromURL = new URL(from, 'resolve://');
290
+ const toURL = new URL(to, 'resolve://');
291
+
292
+ // the authority is host plus userinfo; a relative reference keeps both from `from`
293
+ if (fromURL.protocol !== toURL.protocol || fromURL.host !== toURL.host || fromURL.username !== toURL.username || fromURL.password !== toURL.password || !fromURL.pathname.startsWith('/') || !toURL.pathname.startsWith('/')) {
294
+ return to;
295
+ }
296
+ const fromSegments = fromURL.pathname.split('/').slice(0, -1);
297
+ const toSegments = toURL.pathname.split('/');
298
+ let common = 0;
299
+ while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
300
+ common += 1;
301
+ }
302
+ const segments = [...fromSegments.slice(common).map(() => '..'), ...toSegments.slice(common)];
303
+ // a leading segment with a colon would be parsed as a scheme, and an empty
304
+ // reference (`to` is the directory of `from`) would resolve back to `from`
305
+ if (segments[0].includes(':') || segments.join('/') === '') {
306
+ segments.unshift('.');
307
+ }
308
+ return segments.join('/') + toURL.search + toURL.hash;
309
+ };
310
+
278
311
  /**
279
312
  * Sanitizes/Encodes URI to it's url encoded form.
280
313
  *
@@ -283,7 +316,7 @@ const resolve = (from, to) => {
283
316
  * by first unsatizing it and then performing sanitization again.
284
317
  * @public
285
318
  */
286
- exports.resolve = resolve;
319
+ exports.relative = relative;
287
320
  const sanitize = uri => {
288
321
  if (isFileSystemPath(uri)) {
289
322
  return fromFileSystemPath(toFileSystemPath(uri));
package/src/util/url.mjs CHANGED
@@ -259,6 +259,38 @@ export const resolve = (from, to) => {
259
259
  return resolvedUrl.toString();
260
260
  };
261
261
 
262
+ /**
263
+ * Computes the URI reference that resolves to `to` against `from` — the inverse
264
+ * of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
265
+ * relative to the directory of `from` (its last path segment is the document,
266
+ * not a directory). When the two URIs differ in scheme or authority, or either
267
+ * has no hierarchical path (e.g. a URN), no relative form exists and `to` is
268
+ * returned unchanged.
269
+ * @public
270
+ */
271
+ export const relative = (from, to) => {
272
+ const fromURL = new URL(from, 'resolve://');
273
+ const toURL = new URL(to, 'resolve://');
274
+
275
+ // the authority is host plus userinfo; a relative reference keeps both from `from`
276
+ if (fromURL.protocol !== toURL.protocol || fromURL.host !== toURL.host || fromURL.username !== toURL.username || fromURL.password !== toURL.password || !fromURL.pathname.startsWith('/') || !toURL.pathname.startsWith('/')) {
277
+ return to;
278
+ }
279
+ const fromSegments = fromURL.pathname.split('/').slice(0, -1);
280
+ const toSegments = toURL.pathname.split('/');
281
+ let common = 0;
282
+ while (common < fromSegments.length && common < toSegments.length - 1 && fromSegments[common] === toSegments[common]) {
283
+ common += 1;
284
+ }
285
+ const segments = [...fromSegments.slice(common).map(() => '..'), ...toSegments.slice(common)];
286
+ // a leading segment with a colon would be parsed as a scheme, and an empty
287
+ // reference (`to` is the directory of `from`) would resolve back to `from`
288
+ if (segments[0].includes(':') || segments.join('/') === '') {
289
+ segments.unshift('.');
290
+ }
291
+ return segments.join('/') + toURL.search + toURL.hash;
292
+ };
293
+
262
294
  /**
263
295
  * Sanitizes/Encodes URI to it's url encoded form.
264
296
  *
@@ -491,6 +491,17 @@ export declare interface ReferenceSetOptions {
491
491
  readonly circular?: boolean;
492
492
  }
493
493
 
494
+ /**
495
+ * Computes the URI reference that resolves to `to` against `from` — the inverse
496
+ * of `resolve`: `resolve(from, relative(from, to)) === to`. The reference is
497
+ * relative to the directory of `from` (its last path segment is the document,
498
+ * not a directory). When the two URIs differ in scheme or authority, or either
499
+ * has no hierarchical path (e.g. a URN), no relative form exists and `to` is
500
+ * returned unchanged.
501
+ * @public
502
+ */
503
+ declare const relative: (from: string, to: string) => string;
504
+
494
505
  /**
495
506
  * @public
496
507
  */
@@ -647,6 +658,7 @@ declare namespace url {
647
658
  stripHash,
648
659
  cwd,
649
660
  resolve_2 as resolve,
661
+ relative,
650
662
  sanitize,
651
663
  unsanitize
652
664
  }
@@ -7,7 +7,7 @@ export type { default as DereferenceStrategy, DereferenceStrategyOptions, } from
7
7
  export type { default as File, FileOptions } from '../../../File.ts';
8
8
  export type { default as Reference, ReferenceOptions } from '../../../Reference.ts';
9
9
  export type { default as ReferenceSet, ReferenceSetOptions } from '../../../ReferenceSet.ts';
10
- export type { Arazzo1BundleVisitorOptions } from './visitor.ts';
10
+ export type { Arazzo1BundleVisitorOptions, InputPlacement } from './visitor.ts';
11
11
  export type { ReferenceOptions as ApiDOMReferenceOptions, ReferenceBundleOptions as ApiDOMReferenceBundleOptions, ReferenceDereferenceOptions as ApiDOMReferenceDereferenceOptions, ReferenceParseOptions as ApiDOMReferenceParseOptions, ReferenceResolveOptions as ApiDOMReferenceResolveOptions, } from '../../../options/index.ts';
12
12
  export type { default as Parser, ParserOptions } from '../../../parse/parsers/Parser.ts';
13
13
  export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
@@ -2,7 +2,17 @@ import { Element, ObjectElement, ParseResultElement } from '@speclynx/apidom-dat
2
2
  import { type Path } from '@speclynx/apidom-traverse';
3
3
  import { ArazzoSpecification1Element } from '@speclynx/apidom-ns-arazzo-1';
4
4
  import Reference from '../../../Reference.ts';
5
+ import { type Schema$idIndex } from '../../../dereference/strategies/arazzo-1/selectors/uri.ts';
5
6
  import type { ReferenceOptions } from '../../../options/index.ts';
7
+ /**
8
+ * A bundled JSON Schema resource awaiting placement into the entry document's
9
+ * `components.inputs`.
10
+ * @public
11
+ */
12
+ export interface InputPlacement {
13
+ readonly name: string;
14
+ readonly element: Element;
15
+ }
6
16
  /**
7
17
  * @public
8
18
  */
@@ -11,6 +21,8 @@ export interface Arazzo1BundleVisitorOptions {
11
21
  readonly options: ReferenceOptions;
12
22
  readonly assignments?: Map<string, string>;
13
23
  readonly reservedNames?: Map<string, Set<string>>;
24
+ readonly placements?: InputPlacement[];
25
+ readonly schema$idIndex?: Schema$idIndex;
14
26
  }
15
27
  /**
16
28
  * @public
@@ -46,7 +58,28 @@ declare class Arazzo1BundleVisitor {
46
58
  * components that are still being bundled.
47
59
  */
48
60
  protected readonly reservedNames: Map<string, Set<string>>;
49
- constructor({ reference, options, assignments, reservedNames, }: Arazzo1BundleVisitorOptions);
61
+ /**
62
+ * Embedded resources waiting to be placed into the entry document's
63
+ * `components.inputs`, shared across the entry document and every external
64
+ * document visitor. Placement is deferred until the entry document traversal
65
+ * completes: the traversal walks live content, so placing a resource mid-way
66
+ * would make it descend into the resource and re-bundle its `$ref`s against
67
+ * the entry document's base URI instead of the resource's own.
68
+ */
69
+ protected readonly placements: InputPlacement[];
70
+ /**
71
+ * Index of `$id`-bearing schemas per document, shared across the entry document
72
+ * and every external document visitor of a single bundle run so it never
73
+ * outlives mutations of the documents (e.g. the final placement of inputs).
74
+ */
75
+ protected readonly schema$idIndex: Schema$idIndex;
76
+ constructor({ reference, options, assignments, reservedNames, placements, schema$idIndex, }: Arazzo1BundleVisitorOptions);
77
+ /**
78
+ * Base URI of the entry document — its `$self` when present, otherwise its
79
+ * retrieval URI. It is the base against which a relative `$id` written into
80
+ * `components.inputs` resolves.
81
+ */
82
+ protected get entryBaseURI(): string;
50
83
  protected toBaseURI(uri: string): string;
51
84
  protected toReference(uri: string): Promise<Reference>;
52
85
  /**
@@ -72,8 +105,8 @@ declare class Arazzo1BundleVisitor {
72
105
  protected baseName(element: Element, baseURI: string): string;
73
106
  /**
74
107
  * Computes a collision-free component name within `components.inputs`. A name
75
- * is taken if it's already placed in inputs or reserved by a component that is
76
- * still being bundled (reserved before recursion).
108
+ * is taken if it's already present in the entry document's inputs or reserved
109
+ * by a component that is still being bundled (reserved before recursion).
77
110
  */
78
111
  protected uniqueName(candidate: string): string;
79
112
  /**
@@ -82,14 +115,26 @@ declare class Arazzo1BundleVisitor {
82
115
  * its own component (and origin), so report the rename per severity.
83
116
  */
84
117
  protected reportNameCollision(preferredName: string, componentName: string): void;
118
+ /**
119
+ * The entry document traversal is rooted at its Parse Result (nested
120
+ * traversals are rooted at schema resources), so leaving it marks the moment
121
+ * the whole entry document has been visited and the collected resources can
122
+ * be placed into `components.inputs` without the traversal descending into them.
123
+ * Guarded on the node so that a traversal rooted at any other Parse Result
124
+ * never flushes the shared placements early.
125
+ */
126
+ readonly ParseResultElement: {
127
+ leave: (path: Path<Element>) => void;
128
+ };
85
129
  /**
86
130
  * JSON Schema Objects in Arazzo (used by Input Objects and inline schemas) are
87
131
  * a JSON Schema 2020-12 dialect — the only external reference type Arazzo
88
132
  * defines. They are bundled per the JSON Schema Compound Document rules: the
89
133
  * external schema RESOURCE is embedded verbatim into `components.inputs`
90
- * (carrying its `$id`), and the referencing `$ref` is left UNCHANGED — it
91
- * keeps resolving against the embedded resource's `$id`. This mirrors the
92
- * openapi-3-1 strategy's Schema Object handling.
134
+ * (carrying its `$id`), and the referencing `$ref` is rebased onto that `$id`
135
+ * when it resolves to a different URI (a location-based `$ref` to a
136
+ * self-identifying resource); otherwise it is left unchanged. This mirrors
137
+ * the openapi-3-1 strategy's Schema Object handling.
93
138
  */
94
139
  JSONSchemaElement(path: Path<Element>): Promise<void>;
95
140
  }
@@ -7,7 +7,7 @@ export type { default as DereferenceStrategy, DereferenceStrategyOptions, } from
7
7
  export type { default as File, FileOptions } from '../../../File.ts';
8
8
  export type { default as Reference, ReferenceOptions } from '../../../Reference.ts';
9
9
  export type { default as ReferenceSet, ReferenceSetOptions } from '../../../ReferenceSet.ts';
10
- export type { OpenAPI3_1BundleVisitorOptions } from './visitor.ts';
10
+ export type { OpenAPI3_1BundleVisitorOptions, ComponentPlacement } from './visitor.ts';
11
11
  export type { ReferenceOptions as ApiDOMReferenceOptions, ReferenceBundleOptions as ApiDOMReferenceBundleOptions, ReferenceDereferenceOptions as ApiDOMReferenceDereferenceOptions, ReferenceParseOptions as ApiDOMReferenceParseOptions, ReferenceResolveOptions as ApiDOMReferenceResolveOptions, } from '../../../options/index.ts';
12
12
  export type { default as Parser, ParserOptions } from '../../../parse/parsers/Parser.ts';
13
13
  export type { default as Resolver, ResolverOptions } from '../../../resolve/resolvers/Resolver.ts';
@@ -2,7 +2,18 @@ import { Element, ObjectElement, ParseResultElement } from '@speclynx/apidom-dat
2
2
  import { type Path } from '@speclynx/apidom-traverse';
3
3
  import { OpenApi3_1Element } from '@speclynx/apidom-ns-openapi-3-1';
4
4
  import Reference from '../../../Reference.ts';
5
+ import { type Schema$idIndex } from '../../../dereference/strategies/openapi-3-1/selectors/uri.ts';
5
6
  import type { ReferenceOptions } from '../../../options/index.ts';
7
+ /**
8
+ * A bundled element awaiting placement into the entry document's Components
9
+ * Object field.
10
+ * @public
11
+ */
12
+ export interface ComponentPlacement {
13
+ readonly field: string;
14
+ readonly name: string;
15
+ readonly element: Element;
16
+ }
6
17
  /**
7
18
  * @public
8
19
  */
@@ -12,6 +23,9 @@ export interface OpenAPI3_1BundleVisitorOptions {
12
23
  readonly assignments?: Map<string, string>;
13
24
  readonly reservedNames?: Map<string, Set<string>>;
14
25
  readonly refractCache?: WeakMap<Element, Map<string, Element>>;
26
+ readonly schema$idIndex?: Schema$idIndex;
27
+ readonly placements?: ComponentPlacement[];
28
+ readonly relocationBaseURI?: string;
15
29
  }
16
30
  /**
17
31
  * @public
@@ -54,7 +68,30 @@ declare class OpenAPI3_1BundleVisitor {
54
68
  * generic object referenced once as a Parameter and once as a Response).
55
69
  */
56
70
  protected readonly refractCache: WeakMap<Element, Map<string, Element>>;
57
- constructor({ reference, options, assignments, reservedNames, refractCache, }: OpenAPI3_1BundleVisitorOptions);
71
+ /**
72
+ * Index of `$id`-bearing schemas per document, shared across the entry document
73
+ * and every external document visitor of a single bundle run so it never
74
+ * outlives mutations of the documents (e.g. the final placement of components).
75
+ */
76
+ protected readonly schema$idIndex: Schema$idIndex;
77
+ /**
78
+ * Bundled fragments waiting to be placed into the entry document's
79
+ * Components Object, shared across the entry document and every external
80
+ * document visitor. Placement is deferred until the entry document traversal
81
+ * completes: the traversal walks live content, so placing a fragment mid-way
82
+ * would make it descend into the fragment and re-bundle its `$ref`s against
83
+ * the entry document's base URI instead of the fragment's own.
84
+ */
85
+ protected readonly placements: ComponentPlacement[];
86
+ /**
87
+ * The base URI the traversed content will have once placed into the entry
88
+ * document, when placement changes it: content hoisted out of a non-schema
89
+ * document (a Response, Parameter, Path Item, ...) trades that document's
90
+ * base URI for the entry document's. Unset for the entry document itself and
91
+ * for embedded schema resources, whose `$id` carries their base along.
92
+ */
93
+ protected readonly relocationBaseURI: string | undefined;
94
+ constructor({ reference, options, assignments, reservedNames, refractCache, schema$idIndex, placements, relocationBaseURI, }: OpenAPI3_1BundleVisitorOptions);
58
95
  protected getRefracted(source: Element, type: string): Element | undefined;
59
96
  protected setRefracted(source: Element, type: string, refracted: Element): void;
60
97
  protected toBaseURI(uri: string): string;
@@ -65,6 +102,14 @@ declare class OpenAPI3_1BundleVisitor {
65
102
  * transferable. Bare fragments are returned unchanged.
66
103
  */
67
104
  protected normalizeSelfReference(ref: string): string;
105
+ /**
106
+ * The base URI the given Schema Object's `$ref` will be resolved against once
107
+ * the content is placed into the entry document, or `undefined` when the
108
+ * `$ref` as written resolves to `$refBaseURI` from there as well: the content
109
+ * isn't relocated, an absolute `$id` on the schema's ancestor chain pins its
110
+ * base, or the hoisted document sits next to the entry document.
111
+ */
112
+ protected relocatedSchemaBaseURI(path: Path<Element>, $refBaseURI: string): string | undefined;
68
113
  /**
69
114
  * Handles an error according to the `bundle.continueOnError` option.
70
115
  *
@@ -93,8 +138,8 @@ declare class OpenAPI3_1BundleVisitor {
93
138
  protected baseName(element: Element, field: string, jsonPointer: string, baseURI: string): string;
94
139
  /**
95
140
  * Computes a collision-free component name within the target field. A name is
96
- * taken if it's already placed in components or reserved by a component that
97
- * is still being bundled (reserved before recursion).
141
+ * taken if it's already present in the entry document's components or reserved
142
+ * by a component that is still being bundled (reserved before recursion).
98
143
  */
99
144
  protected uniqueName(candidate: string, field: string): string;
100
145
  /**
@@ -103,6 +148,17 @@ declare class OpenAPI3_1BundleVisitor {
103
148
  * its own component (and origin), so report the rename per severity.
104
149
  */
105
150
  protected reportNameCollision(preferredName: string, componentName: string, field: string): void;
151
+ /**
152
+ * The entry document traversal is rooted at its Parse Result (nested
153
+ * traversals are rooted at fragments), so leaving it marks the moment the
154
+ * whole entry document has been visited and the collected fragments can be
155
+ * placed into its Components Object without the traversal descending into them.
156
+ * Guarded on the node so that a traversal rooted at any other Parse Result
157
+ * never flushes the shared placements early.
158
+ */
159
+ readonly ParseResultElement: {
160
+ leave: (path: Path<Element>) => void;
161
+ };
106
162
  ReferenceElement(path: Path<Element>): Promise<void>;
107
163
  /**
108
164
  * Unlike OpenAPI 3.0 (which has no `components.pathItems` field and therefore
@@ -20,3 +20,42 @@ export declare const sanitizeComponentName: (value: string) => string;
20
20
  * @public
21
21
  */
22
22
  export declare const uniqueName: (candidate: string, isTaken: (name: string) => boolean) => string;
23
+ /**
24
+ * Computes the `$ref` value addressing an embedded JSON Schema resource by its
25
+ * canonical `$id`. `$refURI` is the `$ref` resolved against the referencing
26
+ * document's base URI; `resourceURI` is the embedded resource's canonical URI
27
+ * (its `$id`, or the retrieval URI when it has none).
28
+ *
29
+ * When the two differ — a location-based `$ref` (e.g. `./ex.json#/$defs/Pet`)
30
+ * to a resource that identifies itself differently (`$id: https://example.com/pets`)
31
+ * — the `$ref` is rebased onto the resource URI, keeping its `$anchor` or JSON
32
+ * Pointer fragment. Once embedded, the resource is reachable only by its `$id`,
33
+ * so an unchanged `$ref` would dangle. When they already agree, `undefined` is
34
+ * returned and the `$ref` is to be left as written.
35
+ *
36
+ * This knowingly departs from the letter of JSON Schema 2020-12 §9.3.1
37
+ * ("references ... MUST NOT be changed"), which presumes `$ref`s are written
38
+ * against `$id`s. The target schema — and thus evaluation output — is unchanged;
39
+ * only the alias the loader knew the resource by is normalized to its canonical URI.
40
+ *
41
+ * @public
42
+ */
43
+ export declare const rebaseSchema$ref: ($refURI: string, resourceURI: string) => string | undefined;
44
+ /**
45
+ * Computes the `$ref` value addressing an embedded JSON Schema resource from a
46
+ * Schema Object whose base URI changes when it is relocated into the entry
47
+ * document — one carried inside a hoisted Response, Parameter, Path Item, etc.
48
+ * `$refURI` and `resourceURI` are as in `rebaseSchema$ref`; `baseURI` is the
49
+ * base the `$ref` resolves against after relocation.
50
+ *
51
+ * A resource that identifies itself (`isResourceIdentified`, i.e. it declares a
52
+ * `$id`) is addressed by that `$id`, exactly as `rebaseSchema$ref` does. A
53
+ * resource identified only by its retrieval URI is addressed by a URI reference
54
+ * relative to `baseURI`: it then resolves to the `$id` assigned from that
55
+ * retrieval URI wherever the bundle is placed, and no absolute retrieval
56
+ * location leaks into the `$ref`. The `$anchor` or JSON Pointer fragment is
57
+ * kept in both cases.
58
+ *
59
+ * @public
60
+ */
61
+ export declare const relocateSchema$ref: ($refURI: string, resourceURI: string, baseURI: string, isResourceIdentified: boolean) => string;
@@ -1 +1 @@
1
- export { isAnchor, uriToAnchor, parse, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError, } from '../../openapi-3-1/selectors/$anchor.ts';
1
+ export { isAnchor, uriToAnchor, parse, locate, evaluate, EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError, JsonSchema$anchorError, } from '../../openapi-3-1/selectors/$anchor.ts';
@@ -1 +1 @@
1
- export { evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError, } from '../../openapi-3-1/selectors/uri.ts';
1
+ export { locate, evaluate, EvaluationJsonSchemaUriError, JsonSchemaUriError, type EvaluateOptions, type Schema$idIndex, } from '../../openapi-3-1/selectors/uri.ts';
@@ -1,7 +1,7 @@
1
1
  import { Element, ParseResultElement } from '@speclynx/apidom-datamodel';
2
2
  import { SourceDescriptionElement } from '@speclynx/apidom-ns-arazzo-1';
3
3
  import type Reference from '../../../Reference.ts';
4
- export { resolveSchema$refField, resolveSchema$idField } from '../openapi-3-1/util.ts';
4
+ export { resolveSchema$refField, resolveSchema$idField, resolveSchemaBaseURI, } from '../openapi-3-1/util.ts';
5
5
  /**
6
6
  * Computes the base URI of an Arazzo document per Arazzo 1.1.0 rules.
7
7
  *
@@ -1,5 +1,6 @@
1
1
  import { Element } from '@speclynx/apidom-datamodel';
2
2
  import { type Path } from '@speclynx/apidom-traverse';
3
+ import { type Schema$idIndex } from './selectors/uri.ts';
3
4
  import Reference from '../../../Reference.ts';
4
5
  import { AncestorLineage } from '../../util.ts';
5
6
  import type { ReferenceOptions } from '../../../options/index.ts';
@@ -10,8 +11,10 @@ export interface Arazzo1DereferenceVisitorOptions {
10
11
  readonly reference: Reference;
11
12
  readonly options: ReferenceOptions;
12
13
  readonly indirections?: Element[];
14
+ readonly schema$idIndex?: Schema$idIndex;
13
15
  readonly ancestors?: AncestorLineage<Element>;
14
16
  readonly visited?: WeakSet<Element>;
17
+ readonly ancestorSchema$ids?: string[];
15
18
  }
16
19
  /**
17
20
  * @public
@@ -26,14 +29,27 @@ declare class Arazzo1DereferenceVisitor {
26
29
  */
27
30
  protected readonly baseURI: string;
28
31
  protected readonly options: ReferenceOptions;
32
+ /**
33
+ * Index of `$id`-bearing schemas per document, shared across nested visitors
34
+ * of a single dereference run so it never outlives mutations of the documents.
35
+ */
36
+ protected readonly schema$idIndex: Schema$idIndex;
29
37
  protected readonly visited: WeakSet<Element>;
38
+ /**
39
+ * `$id`s of the JSON Schema elements enclosing the traversal root within its
40
+ * document (outermost first). Empty when the root is the document itself; a
41
+ * nested visitor traversing a fragment is seeded with the `$id`s of the
42
+ * schemas the fragment sits under, so the `$ref`s and `$id`s within resolve
43
+ * against the same base as they would from the document root.
44
+ */
45
+ protected readonly ancestorSchema$ids: string[];
30
46
  /**
31
47
  * Tracks element ancestors across dive-deep traversal boundaries.
32
48
  * Used for cycle detection: if a referenced element is found in
33
49
  * the ancestor lineage, a circular reference is detected.
34
50
  */
35
51
  protected readonly ancestors: AncestorLineage<Element>;
36
- constructor({ reference, options, indirections, ancestors, visited, }: Arazzo1DereferenceVisitorOptions);
52
+ constructor({ reference, options, indirections, schema$idIndex, ancestors, visited, ancestorSchema$ids, }: Arazzo1DereferenceVisitorOptions);
37
53
  protected toAncestorLineage(path: Path<Element>): [AncestorLineage<Element>, Set<Element>];
38
54
  protected toBaseURI(uri: string): string;
39
55
  protected toReference(uri: string): Promise<Reference>;
@@ -1,4 +1,5 @@
1
1
  import { Element } from '@speclynx/apidom-datamodel';
2
+ import { type SchemaLocation } from '../util.ts';
2
3
  import EvaluationJsonSchema$anchorError from '../../../../errors/EvaluationJsonSchema$anchorError.ts';
3
4
  import InvalidJsonSchema$anchorError from '../../../../errors/InvalidJsonSchema$anchorError.ts';
4
5
  /**
@@ -13,10 +14,17 @@ export declare const uriToAnchor: (uri: string) => string;
13
14
  * @public
14
15
  */
15
16
  export declare const parse: (anchor: string) => string;
17
+ /**
18
+ * Locates the schema identified by JSON Schema $anchor within ApiDOM fragment,
19
+ * along with the `$id`s of the schemas enclosing it, the search root included.
20
+ * @public
21
+ */
22
+ export declare const locate: <T extends Element>(anchor: string, element: T) => SchemaLocation;
16
23
  /**
17
24
  * Evaluates JSON Schema $anchor against ApiDOM fragment.
18
25
  * @public
19
26
  */
20
27
  export declare const evaluate: <T extends Element>(anchor: string, element: T) => Element | undefined;
28
+ export type { SchemaLocation } from '../util.ts';
21
29
  export { EvaluationJsonSchema$anchorError, InvalidJsonSchema$anchorError };
22
30
  export { default as JsonSchema$anchorError } from '../../../../errors/JsonSchema$anchorError.ts';
@@ -0,0 +1,9 @@
1
+ import { Element } from '@speclynx/apidom-datamodel';
2
+ import { type JSONPointer } from '@speclynx/apidom-json-pointer';
3
+ import { type SchemaLocation } from '../util.ts';
4
+ /**
5
+ * Locates the element addressed by JSON Pointer within ApiDOM fragment, along
6
+ * with the `$id`s of the schemas enclosing it: the elements the pointer walks
7
+ * through on its way to the target, the search root included.
8
+ */
9
+ export declare const locate: <T extends Element>(element: T, jsonPointer: JSONPointer) => SchemaLocation;
@@ -1,12 +1,45 @@
1
1
  import { Element } from '@speclynx/apidom-datamodel';
2
+ import { JSONSchemaElement } from '@speclynx/apidom-ns-json-schema-2020-12';
2
3
  import EvaluationJsonSchemaUriError from '../../../../errors/EvaluationJsonSchemaUriError.ts';
4
+ import { type SchemaLocation } from '../util.ts';
5
+ /**
6
+ * Index of `$id`-bearing schemas per indexed element (a document root, or the
7
+ * fragment a visitor traverses). Each entry pairs a schema with the `$id`s of its
8
+ * enclosing schema resources (outermost first, including its own). The index is
9
+ * populated once per element and never refreshed, so callers scope it
10
+ * to a single dereference/bundle run instead of sharing it globally. Within a run the
11
+ * `$id` graph of an indexed document is stable: the bundle strategies mutate deep
12
+ * clones and place components only after the entry traversal ends, and the
13
+ * dereference strategies only replace `$ref`-bearing schemas.
14
+ * @public
15
+ */
16
+ export type Schema$idIndex = WeakMap<Element, {
17
+ schema: JSONSchemaElement;
18
+ $ids: string[];
19
+ }[]>;
20
+ /**
21
+ * @public
22
+ */
23
+ export interface EvaluateOptions {
24
+ /**
25
+ * URI the `$id`s of the document resolve against (its retrieval URI or `$self`).
26
+ * Defaults to the evaluated URI itself, which only matches schemas identified by an
27
+ * absolute `$id`; pass the document's base URI to match relative `$id`s correctly.
28
+ */
29
+ readonly baseURI?: string;
30
+ readonly index?: Schema$idIndex;
31
+ }
32
+ /**
33
+ * Locates the schema addressed by JSON Schema $ref containing unknown URI within
34
+ * ApiDOM fragment, along with the `$id`s of the schemas enclosing it.
35
+ * @public
36
+ */
37
+ export declare const locate: <T extends Element>(uri: string, element: T, options?: EvaluateOptions) => SchemaLocation;
3
38
  /**
4
39
  * Evaluates JSON Schema $ref containing unknown URI against ApiDOM fragment.
5
40
  * @public
6
41
  */
7
- export declare const evaluate: {
8
- <T extends Element>(uri: string, element: T): Element | undefined;
9
- cache: WeakMap<WeakKey, any>;
10
- };
42
+ export declare const evaluate: <T extends Element>(uri: string, element: T, options?: EvaluateOptions) => Element | undefined;
43
+ export type { SchemaLocation } from '../util.ts';
11
44
  export { EvaluationJsonSchemaUriError };
12
45
  export { default as JsonSchemaUriError } from '../../../../errors/JsonSchemaUriError.ts';
@@ -1,13 +1,64 @@
1
1
  import { Element } from '@speclynx/apidom-datamodel';
2
+ import type { Path } from '@speclynx/apidom-traverse';
2
3
  import { JSONSchemaElement } from '@speclynx/apidom-ns-json-schema-2020-12';
3
4
  /**
5
+ * A schema located within a document, together with the `$id`s of the JSON Schema
6
+ * elements enclosing it (outermost first; the located schema's own `$id` excluded).
4
7
  * @public
5
8
  */
6
- export declare const resolveSchema$refField: (retrievalURI: string, schemaElement: JSONSchemaElement) => string | undefined;
9
+ export interface SchemaLocation {
10
+ readonly element: Element;
11
+ readonly ancestorSchema$ids: string[];
12
+ }
7
13
  /**
14
+ * Whether the element is a JSON Schema element declaring a `$id`.
15
+ */
16
+ export declare const isJSONSchemaElementWith$id: (element: unknown) => element is JSONSchemaElement;
17
+ /**
18
+ * `$id`s declared by the JSON Schema elements among `nodes`, in order.
19
+ */
20
+ export declare const schema$idsOf: (nodes: readonly unknown[]) => string[];
21
+ /**
22
+ * `$id`s of the JSON Schema elements on `path` (outermost first, the node itself
23
+ * included). The `$id`s are read from the elements themselves rather than from
24
+ * refraction-time metadata, so a `$id` assigned after refraction (e.g. by the
25
+ * bundler) is honored.
26
+ */
27
+ export declare const collectSchema$ids: (path: Path<Element>) => string[];
28
+ /**
29
+ * `$id`s of the schemas enclosing a traversal root that is a fragment detached
30
+ * from its document, read from the `ancestorsSchemaIdentifiers` meta the JSON
31
+ * Schema refractors record. The meta also carries the root's own `$id`, which
32
+ * the ancestor walk contributes itself, so it is dropped. A detached fragment
33
+ * has no live ancestors, so the meta is the only carrier of its lexical
34
+ * context; this is the one reader of it the refractors must keep serving.
35
+ */
36
+ export declare const detachedRootSchema$ids: (root: Element | undefined) => string[];
37
+ /**
38
+ * Resolves a chain of `$id`s (outermost first) against `baseURI`, each `$id`
39
+ * refining the base the next one resolves against.
40
+ */
41
+ export declare const resolveSchema$ids: (baseURI: string, $ids: readonly string[]) => string;
42
+ /**
43
+ * Resolves the base URI of the schema at `path`: `baseURI` (the base URI in
44
+ * effect at the traversal root) refined by each `$id` on the schema's ancestor
45
+ * chain in turn, the schema's own `$id` included.
46
+ */
47
+ export declare const resolveSchemaBaseURI: (baseURI: string, path: Path<Element>) => string;
48
+ /**
49
+ * Resolves the `$ref` of the schema against its base URI (see
50
+ * `resolveSchemaBaseURI`), or returns `undefined` when the schema has no string `$ref`.
51
+ *
52
+ * @public
53
+ */
54
+ export declare const resolveSchema$refField: (schemaBaseURI: string, schemaElement: JSONSchemaElement) => string | undefined;
55
+ /**
56
+ * Resolves the `$id` of the schema against `baseURI`, the base URI in effect
57
+ * where the schema sits, or returns `undefined` when the schema declares no string `$id`.
58
+ *
8
59
  * @public
9
60
  */
10
- export declare const resolveSchema$idField: (retrievalURI: string, schemaElement: JSONSchemaElement) => string | undefined;
61
+ export declare const resolveSchema$idField: (baseURI: string, schemaElement: JSONSchemaElement) => string | undefined;
11
62
  /**
12
63
  * Cached version of SchemaElement.refract.
13
64
  */