fork of hey-api/openapi-ts because I need some additional things
1import { ono } from '@jsdevtools/ono';
2import type { JSONSchema4Type, JSONSchema6Type, JSONSchema7Type } from 'json-schema';
3
4import type { ParserOptions } from './options';
5import $Ref from './ref';
6import type { JSONSchema } from './types';
7import convertPathToPosix from './util/convert-path-to-posix';
8import * as url from './util/url';
9
10interface $RefsMap<S extends object = JSONSchema> {
11 [url: string]: $Ref<S>;
12}
13/**
14 * When you call the resolve method, the value that gets passed to the callback function (or Promise) is a $Refs object. This same object is accessible via the parser.$refs property of $RefParser objects.
15 *
16 * This object is a map of JSON References and their resolved values. It also has several convenient helper methods that make it easy for you to navigate and manipulate the JSON References.
17 *
18 * See https://apitools.dev/json-schema-ref-parser/docs/refs.html
19 */
20export default class $Refs<S extends object = JSONSchema> {
21 /**
22 * This property is true if the schema contains any circular references. You may want to check this property before serializing the dereferenced schema as JSON, since JSON.stringify() does not support circular references by default.
23 *
24 * See https://apitools.dev/json-schema-ref-parser/docs/refs.html#circular
25 */
26 public circular: boolean;
27
28 /**
29 * Returns the paths/URLs of all the files in your schema (including the main schema file).
30 *
31 * See https://apitools.dev/json-schema-ref-parser/docs/refs.html#pathstypes
32 *
33 * @param types (optional) Optionally only return certain types of paths ("file", "http", etc.)
34 */
35 paths(...types: (string | string[])[]): string[] {
36 const paths = getPaths(this._$refs, types.flat());
37 return paths.map((path) => convertPathToPosix(path.decoded));
38 }
39
40 /**
41 * Returns a map of paths/URLs and their correspond values.
42 *
43 * See https://apitools.dev/json-schema-ref-parser/docs/refs.html#valuestypes
44 *
45 * @param types (optional) Optionally only return values from certain locations ("file", "http", etc.)
46 */
47 values(...types: (string | string[])[]): S {
48 const $refs = this._$refs;
49 const paths = getPaths($refs, types.flat());
50 return paths.reduce<Record<string, any>>((obj, path) => {
51 obj[convertPathToPosix(path.decoded)] = $refs[path.encoded]!.value;
52 return obj;
53 }, {}) as S;
54 }
55
56 /**
57 * Returns `true` if the given path exists in the schema; otherwise, returns `false`
58 *
59 * See https://apitools.dev/json-schema-ref-parser/docs/refs.html#existsref
60 *
61 * @param $ref The JSON Reference path, optionally with a JSON Pointer in the hash
62 */
63 /**
64 * Determines whether the given JSON reference exists.
65 *
66 * @param path - The path being resolved, optionally with a JSON pointer in the hash
67 * @param [options]
68 * @returns
69 */
70 exists(path: string, options: any) {
71 try {
72 this._resolve(path, '', options);
73 return true;
74 } catch {
75 return false;
76 }
77 }
78
79 /**
80 * Resolves the given JSON reference and returns the resolved value.
81 *
82 * @param path - The path being resolved, with a JSON pointer in the hash
83 * @param [options]
84 * @returns - Returns the resolved value
85 */
86 get(path: string, options?: ParserOptions): JSONSchema4Type | JSONSchema6Type | JSONSchema7Type {
87 return this._resolve(path, '', options)!.value;
88 }
89
90 /**
91 * Sets the value at the given path in the schema. If the property, or any of its parents, don't exist, they will be created.
92 *
93 * @param path The JSON Reference path, optionally with a JSON Pointer in the hash
94 * @param value The value to assign. Can be anything (object, string, number, etc.)
95 */
96 set(path: string, value: JSONSchema4Type | JSONSchema6Type | JSONSchema7Type) {
97 const absPath = url.resolve(this._root$Ref.path!, path);
98 const withoutHash = url.stripHash(absPath);
99 const $ref = this._$refs[withoutHash];
100
101 if (!$ref) {
102 throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
103 }
104
105 $ref.set(absPath, value);
106 }
107 /**
108 * Returns the specified {@link $Ref} object, or undefined.
109 *
110 * @param path - The path being resolved, optionally with a JSON pointer in the hash
111 * @returns
112 * @protected
113 */
114 _get$Ref(path: string) {
115 path = url.resolve(this._root$Ref.path!, path);
116 const withoutHash = url.stripHash(path);
117 return this._$refs[withoutHash];
118 }
119
120 /**
121 * Creates a new {@link $Ref} object and adds it to this {@link $Refs} object.
122 *
123 * @param path - The file path or URL of the referenced file
124 */
125 _add(path: string) {
126 const withoutHash = url.stripHash(path);
127
128 const $ref = new $Ref<S>(this);
129 $ref.path = withoutHash;
130
131 this._$refs[withoutHash] = $ref;
132 this._root$Ref = this._root$Ref || $ref;
133
134 return $ref;
135 }
136
137 /**
138 * Resolves the given JSON reference.
139 *
140 * @param path - The path being resolved, optionally with a JSON pointer in the hash
141 * @param pathFromRoot - The path of `obj` from the schema root
142 * @param [options]
143 * @returns
144 * @protected
145 */
146 _resolve(path: string, pathFromRoot: string, options?: ParserOptions) {
147 const absPath = url.resolve(this._root$Ref.path!, path);
148 const withoutHash = url.stripHash(absPath);
149 const $ref = this._$refs[withoutHash];
150
151 if (!$ref) {
152 throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`);
153 }
154
155 if ($ref.value === undefined) {
156 console.warn(`$ref entry exists but value is undefined: ${withoutHash}`);
157 return null; // Treat as unresolved
158 }
159
160 return $ref.resolve(absPath, options, path, pathFromRoot);
161 }
162
163 /**
164 * A map of paths/urls to {@link $Ref} objects
165 *
166 * @type {object}
167 * @protected
168 */
169 _$refs: $RefsMap<S> = {};
170
171 /**
172 * The {@link $Ref} object that is the root of the JSON schema.
173 *
174 * @type {$Ref}
175 * @protected
176 */
177 _root$Ref: $Ref<S>;
178
179 constructor() {
180 /**
181 * Indicates whether the schema contains any circular references.
182 *
183 * @type {boolean}
184 */
185 this.circular = false;
186
187 this._$refs = {};
188
189 // @ts-ignore
190 this._root$Ref = null;
191 }
192
193 /**
194 * Returns the paths of all the files/URLs that are referenced by the JSON schema,
195 * including the schema itself.
196 *
197 * @param [types] - Only return paths of the given types ("file", "http", etc.)
198 * @returns
199 */
200 /**
201 * Returns the map of JSON references and their resolved values.
202 *
203 * @param [types] - Only return references of the given types ("file", "http", etc.)
204 * @returns
205 */
206
207 /**
208 * Returns a POJO (plain old JavaScript object) for serialization as JSON.
209 *
210 * @returns {object}
211 */
212 toJSON = this.values;
213}
214
215/**
216 * Returns the encoded and decoded paths keys of the given object.
217 *
218 * @param $refs - The object whose keys are URL-encoded paths
219 * @param [types] - Only return paths of the given types ("file", "http", etc.)
220 * @returns
221 */
222function getPaths<S extends object = JSONSchema>($refs: $RefsMap<S>, types: string[]) {
223 let paths = Object.keys($refs);
224
225 // Filter the paths by type
226 types = Array.isArray(types[0]) ? types[0] : Array.prototype.slice.call(types);
227 if (types.length > 0 && types[0]) {
228 paths = paths.filter((key) => types.includes($refs[key]!.pathType as string));
229 }
230
231 // Decode local filesystem paths
232 return paths.map((path) => ({
233 decoded: $refs[path]!.pathType === 'file' ? url.toFileSystemPath(path, true) : path,
234 encoded: path,
235 }));
236}