fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: maximum call stack exceeded

Lubos 072a9ae7 ebd818da

+130 -273
+5
.changeset/smooth-maps-repeat.md
··· 1 + --- 2 + "@hey-api/json-schema-ref-parser": patch 3 + --- 4 + 5 + **fix**: pass seen references through crawl stack
+2 -1
dev/inputs.ts
··· 9 9 opencode: path.resolve(getSpecsPath(), '3.1.x', 'opencode.yaml'), 10 10 petstore: 11 11 'https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml', 12 - redfish: 'http://redfish.dmtf.org/schemas/v1/Message.v1_2_1.yaml', 12 + redfish: 13 + 'https://raw.githubusercontent.com/DMTF/Redfish-Publications/refs/heads/main/openapi/openapi.yaml', 13 14 scalar: 'scalar:@scalar/access-service', 14 15 transformers: path.resolve(getSpecsPath(), '3.1.x', 'transformers.json'), 15 16 validators: path.resolve(getSpecsPath(), '3.1.x', 'validators.yaml'),
-14
packages/json-schema-ref-parser/README.md
··· 58 58 instance 59 59 - Compatible with Node LTS and beyond, and all major web browsers on Windows, Mac, and Linux 60 60 61 - ## Example 62 - 63 - ```javascript 64 - import { $RefParser } from '@hey-api/json-schema-ref-parser'; 65 - 66 - try { 67 - const parser = new $RefParser(); 68 - await parser.dereference({ pathOrUrlOrSchema: mySchema }); 69 - console.log(parser.schema.definitions.person.properties.firstName); 70 - } catch (err) { 71 - console.error(err); 72 - } 73 - ``` 74 - 75 61 ### New in this fork (@hey-api) 76 62 77 63 - **Multiple inputs with `bundleMany`**: Merge and bundle several OpenAPI/JSON Schema inputs (files, URLs, or raw objects) into a single schema. Components are prefixed to avoid name collisions, paths are namespaced on conflict, and `$ref`s are rewritten accordingly.
+20 -122
packages/json-schema-ref-parser/src/bundle.ts
··· 4 4 import $Ref from './ref'; 5 5 import type $Refs from './refs'; 6 6 import type { JSONSchema } from './types'; 7 + import { MissingPointerError } from './util/errors'; 7 8 import * as url from './util/url'; 8 9 9 - const DEBUG_PERFORMANCE = 10 - process.env.DEBUG === 'true' || 11 - (typeof globalThis !== 'undefined' && (globalThis as any).DEBUG_BUNDLE_PERFORMANCE === true); 12 - 13 - const perf = { 14 - log: (message: string, ...args: any[]) => 15 - DEBUG_PERFORMANCE && console.log('[PERF] ' + message, ...args), 16 - mark: (name: string) => DEBUG_PERFORMANCE && performance.mark(name), 17 - measure: (name: string, start: string, end: string) => 18 - DEBUG_PERFORMANCE && performance.measure(name, start, end), 19 - warn: (message: string, ...args: any[]) => 20 - DEBUG_PERFORMANCE && console.warn('[PERF] ' + message, ...args), 21 - }; 22 - 23 10 export interface InventoryEntry { 24 11 $ref: any; 25 12 circular: any; ··· 43 30 const lookup = new Map<string, InventoryEntry>(); 44 31 const objectIds = new WeakMap<object, string>(); // Use WeakMap to avoid polluting objects 45 32 let idCounter = 0; 46 - let lookupCount = 0; 47 - let addCount = 0; 48 33 49 34 const getObjectId = (obj: any) => { 50 35 if (!objectIds.has(obj)) { ··· 59 44 60 45 return { 61 46 add: (entry: InventoryEntry) => { 62 - addCount++; 63 47 const key = createInventoryKey(entry.parent, entry.key); 64 48 lookup.set(key, entry); 65 - if (addCount % 100 === 0) { 66 - perf.log(`Inventory lookup: Added ${addCount} entries, map size: ${lookup.size}`); 67 - } 68 49 }, 69 50 find: ($refParent: any, $refKey: any) => { 70 - lookupCount++; 71 51 const key = createInventoryKey($refParent, $refKey); 72 52 const result = lookup.get(key); 73 - if (lookupCount % 100 === 0) { 74 - perf.log(`Inventory lookup: ${lookupCount} lookups performed`); 75 - } 76 53 return result; 77 54 }, 78 - getStats: () => ({ addCount, lookupCount, mapSize: lookup.size }), 79 55 remove: (entry: InventoryEntry) => { 80 56 const key = createInventoryKey(entry.parent, entry.key); 81 57 lookup.delete(key); ··· 171 147 */ 172 148 visitedObjects?: WeakSet<object>; 173 149 }) => { 174 - perf.mark('inventory-ref-start'); 175 150 const $ref = $refKey === null ? $refParent : $refParent[$refKey]; 176 151 const $refPath = url.resolve(path, $ref.$ref); 177 152 178 153 // Check cache first to avoid redundant resolution 179 154 let pointer = resolvedRefs.get($refPath); 180 155 if (!pointer) { 181 - perf.mark('resolve-start'); 182 - pointer = $refs._resolve($refPath, pathFromRoot, options); 183 - perf.mark('resolve-end'); 184 - perf.measure('resolve-time', 'resolve-start', 'resolve-end'); 156 + try { 157 + pointer = $refs._resolve($refPath, pathFromRoot, options); 158 + } catch (error) { 159 + if (error instanceof MissingPointerError) { 160 + // Log warning but continue - common in complex schema ecosystems 161 + console.warn(`Skipping unresolvable $ref: ${$refPath}`); 162 + return; 163 + } 164 + throw error; // Re-throw unexpected errors 165 + } 185 166 186 167 if (pointer) { 187 168 resolvedRefs.set($refPath, pointer); 188 - perf.log(`Cached resolved $ref: ${$refPath}`); 189 169 } 190 170 } 191 171 192 - if (pointer === null) { 193 - perf.mark('inventory-ref-end'); 194 - perf.measure('inventory-ref-time', 'inventory-ref-start', 'inventory-ref-end'); 195 - return; 196 - } 172 + if (pointer === null) return; 197 173 198 174 const parsed = Pointer.parse(pathFromRoot); 199 175 const depth = parsed.length; ··· 204 180 indirections += pointer.indirections; 205 181 206 182 // Check if this exact location (parent + key + pathFromRoot) has already been inventoried 207 - perf.mark('lookup-start'); 208 183 const existingEntry = inventoryLookup.find($refParent, $refKey); 209 - perf.mark('lookup-end'); 210 - perf.measure('lookup-time', 'lookup-start', 'lookup-end'); 211 184 212 185 if (existingEntry && existingEntry.pathFromRoot === pathFromRoot) { 213 186 // This exact location has already been inventoried, so we don't need to process it again ··· 215 188 removeFromInventory(inventory, existingEntry); 216 189 inventoryLookup.remove(existingEntry); 217 190 } else { 218 - perf.mark('inventory-ref-end'); 219 - perf.measure('inventory-ref-time', 'inventory-ref-start', 'inventory-ref-end'); 220 191 return; 221 192 } 222 193 } ··· 246 217 inventory.push(newEntry); 247 218 inventoryLookup.add(newEntry); 248 219 249 - perf.log( 250 - `Inventoried $ref: ${$ref.$ref} -> ${file}${hash} (external: ${external}, depth: ${depth})`, 251 - ); 252 - 253 220 // Recursively crawl the resolved value 254 221 if (!existingEntry || external) { 255 - perf.mark('crawl-recursive-start'); 256 222 crawl({ 257 223 $refs, 258 224 indirections: indirections + 1, ··· 266 232 resolvedRefs, 267 233 visitedObjects, 268 234 }); 269 - perf.mark('crawl-recursive-end'); 270 - perf.measure('crawl-recursive-time', 'crawl-recursive-start', 'crawl-recursive-end'); 271 235 } 272 - 273 - perf.mark('inventory-ref-end'); 274 - perf.measure('inventory-ref-time', 'inventory-ref-start', 'inventory-ref-end'); 275 236 }; 276 237 277 238 /** ··· 330 291 331 292 if (obj && typeof obj === 'object' && !ArrayBuffer.isView(obj)) { 332 293 // Early exit if we've already processed this exact object 333 - if (visitedObjects.has(obj)) { 334 - perf.log(`Skipping already visited object at ${pathFromRoot}`); 335 - return; 336 - } 294 + if (visitedObjects.has(obj)) return; 337 295 338 296 if ($Ref.isAllowed$Ref(obj)) { 339 - perf.log(`Found $ref at ${pathFromRoot}: ${(obj as any).$ref}`); 340 297 inventory$Ref({ 341 298 $refKey: key, 342 299 $refParent: parent, ··· 369 326 // This produces the shortest possible bundled references 370 327 return a.length - b.length; 371 328 } 372 - }) as (keyof typeof obj)[]; 329 + }) as Array<keyof typeof obj>; 373 330 374 331 for (const key of keys) { 375 332 const keyPath = Pointer.join(path, key); ··· 414 371 * Remap external refs by hoisting resolved values into a shared container in the root schema 415 372 * and pointing all occurrences to those internal definitions. Internal refs remain internal. 416 373 */ 417 - function remap(parser: $RefParser, inventory: InventoryEntry[]) { 418 - perf.log(`Starting remap with ${inventory.length} inventory entries`); 419 - perf.mark('remap-start'); 374 + function remap(parser: $RefParser, inventory: Array<InventoryEntry>) { 420 375 const root = parser.schema as any; 421 376 422 377 // Group & sort all the $ref pointers, so they're in the order that we need to dereference/remap them 423 - perf.mark('sort-inventory-start'); 424 378 inventory.sort((a: InventoryEntry, b: InventoryEntry) => { 425 379 if (a.file !== b.file) { 426 380 // Group all the $refs that point to the same file ··· 454 408 } 455 409 } 456 410 }); 457 - 458 - perf.mark('sort-inventory-end'); 459 - perf.measure('sort-inventory-time', 'sort-inventory-start', 'sort-inventory-end'); 460 - 461 - perf.log(`Sorted ${inventory.length} inventory entries`); 462 411 463 412 // Ensure or return a container by component type. Prefer OpenAPI-aware placement; 464 413 // otherwise use existing root containers; otherwise create components/*. ··· 583 532 used.add(name); 584 533 return name; 585 534 }; 586 - perf.mark('remap-loop-start'); 587 535 for (const entry of inventory) { 588 536 // Safety check: ensure entry and entry.$ref are valid objects 589 537 if (!entry || !entry.$ref || typeof entry.$ref !== 'object') { 590 - perf.warn(`Skipping invalid inventory entry:`, entry); 591 538 continue; 592 539 } 593 540 ··· 654 601 entry.parent[entry.key] = { $ref: refPath }; 655 602 } 656 603 } 657 - perf.mark('remap-loop-end'); 658 - perf.measure('remap-loop-time', 'remap-loop-start', 'remap-loop-end'); 659 - 660 - perf.mark('remap-end'); 661 - perf.measure('remap-total-time', 'remap-start', 'remap-end'); 662 - 663 - perf.log(`Completed remap of ${inventory.length} entries`); 664 604 } 665 605 666 - function removeFromInventory(inventory: InventoryEntry[], entry: any) { 606 + function removeFromInventory(inventory: Array<InventoryEntry>, entry: any) { 667 607 const index = inventory.indexOf(entry); 668 608 inventory.splice(index, 1); 669 609 } ··· 676 616 * @param parser 677 617 * @param options 678 618 */ 679 - export const bundle = (parser: $RefParser, options: ParserOptions) => { 680 - // console.log('Bundling $ref pointers in %s', parser.$refs._root$Ref.path); 681 - perf.mark('bundle-start'); 682 - 683 - // Build an inventory of all $ref pointers in the JSON Schema 684 - const inventory: InventoryEntry[] = []; 619 + export function bundle(parser: $RefParser, options: ParserOptions): void { 620 + const inventory: Array<InventoryEntry> = []; 685 621 const inventoryLookup = createInventoryLookup(); 686 622 687 - perf.log('Starting crawl phase'); 688 - perf.mark('crawl-phase-start'); 689 - 690 623 const visitedObjects = new WeakSet<object>(); 691 - const resolvedRefs = new Map<string, any>(); // Cache for resolved $ref targets 624 + const resolvedRefs = new Map<string, any>(); 692 625 693 626 crawl<JSONSchema>({ 694 627 $refs: parser.$refs, ··· 704 637 visitedObjects, 705 638 }); 706 639 707 - perf.mark('crawl-phase-end'); 708 - perf.measure('crawl-phase-time', 'crawl-phase-start', 'crawl-phase-end'); 709 - 710 - const stats = inventoryLookup.getStats(); 711 - perf.log(`Crawl phase complete. Found ${inventory.length} $refs. Lookup stats:`, stats); 712 - 713 - // Remap all $ref pointers 714 - perf.log('Starting remap phase'); 715 - perf.mark('remap-phase-start'); 716 640 remap(parser, inventory); 717 - perf.mark('remap-phase-end'); 718 - perf.measure('remap-phase-time', 'remap-phase-start', 'remap-phase-end'); 719 - 720 - perf.mark('bundle-end'); 721 - perf.measure('bundle-total-time', 'bundle-start', 'bundle-end'); 722 - 723 - perf.log('Bundle complete. Performance summary:'); 724 - 725 - // Log final stats 726 - const finalStats = inventoryLookup.getStats(); 727 - perf.log(`Final inventory stats:`, finalStats); 728 - perf.log(`Resolved refs cache size: ${resolvedRefs.size}`); 729 - 730 - if (DEBUG_PERFORMANCE) { 731 - // Log all performance measures 732 - const measures = performance.getEntriesByType('measure'); 733 - measures.forEach((measure) => { 734 - if (measure.name.includes('time')) { 735 - console.log(`${measure.name}: ${measure.duration.toFixed(2)}ms`); 736 - } 737 - }); 738 - 739 - // Clear performance marks and measures for next run 740 - performance.clearMarks(); 741 - performance.clearMeasures(); 742 - } 743 - }; 641 + }
-28
packages/json-schema-ref-parser/src/dereference.ts
··· 1 1 import { ono } from '@jsdevtools/ono'; 2 2 3 - import type { $RefParser } from '.'; 4 3 import type { DereferenceOptions, ParserOptions } from './options'; 5 4 import Pointer from './pointer'; 6 5 import $Ref from './ref'; ··· 8 7 import type { JSONSchema } from './types'; 9 8 import { TimeoutError } from './util/errors'; 10 9 import * as url from './util/url'; 11 - 12 - export default dereference; 13 - 14 - /** 15 - * Crawls the JSON schema, finds all JSON references, and dereferences them. 16 - * This method mutates the JSON schema object, replacing JSON references with their resolved value. 17 - * 18 - * @param parser 19 - * @param options 20 - */ 21 - function dereference(parser: $RefParser, options: ParserOptions) { 22 - const start = Date.now(); 23 - // console.log('Dereferencing $ref pointers in %s', parser.$refs._root$Ref.path); 24 - const dereferenced = crawl<JSONSchema>( 25 - parser.schema, 26 - parser.$refs._root$Ref.path!, 27 - '#', 28 - new Set(), 29 - new Set(), 30 - new Map(), 31 - parser.$refs, 32 - options, 33 - start, 34 - ); 35 - parser.$refs.circular = dereferenced.circular; 36 - parser.schema = dereferenced.value; 37 - } 38 10 39 11 /** 40 12 * Recursively crawls the given value, and dereferences any JSON references.
+13 -49
packages/json-schema-ref-parser/src/index.ts
··· 1 1 import { ono } from '@jsdevtools/ono'; 2 2 3 3 import { bundle as _bundle } from './bundle'; 4 - import _dereference from './dereference'; 5 4 import { getJsonSchemaRefParserDefaultOptions } from './options'; 6 5 import { newFile, parseFile } from './parse'; 7 6 import $Refs from './refs'; ··· 18 17 type: 'file' | 'json' | 'url'; 19 18 } 20 19 21 - export const getResolvedInput = ({ 20 + export function getResolvedInput({ 22 21 pathOrUrlOrSchema, 23 22 }: { 24 23 pathOrUrlOrSchema: JSONSchema | string | unknown; 25 - }): ResolvedInput => { 24 + }): ResolvedInput { 26 25 if (!pathOrUrlOrSchema) { 27 26 throw ono(`Expected a file path, URL, or object. Got ${pathOrUrlOrSchema}`); 28 27 } ··· 61 60 } 62 61 63 62 return resolvedInput; 64 - }; 63 + } 65 64 66 65 // NOTE: previously used helper removed as unused 67 66 ··· 163 162 } 164 163 165 164 /** 166 - * Dereferences all `$ref` pointers in the JSON Schema, replacing each reference with its resolved value. This results in a schema object that does not contain any `$ref` pointers. Instead, it's a normal JavaScript object tree that can easily be crawled and used just like any other JavaScript object. This is great for programmatic usage, especially when using tools that don't understand JSON references. 167 - * 168 - * The dereference method maintains object reference equality, meaning that all `$ref` pointers that point to the same object will be replaced with references to the same object. Again, this is great for programmatic usage, but it does introduce the risk of circular references, so be careful if you intend to serialize the schema using `JSON.stringify()`. Consider using the bundle method instead, which does not create circular references. 169 - * 170 - * See https://apitools.dev/json-schema-ref-parser/docs/ref-parser.html#dereferenceschema-options-callback 171 - * 172 - * @param pathOrUrlOrSchema A JSON Schema object, or the file path or URL of a JSON Schema file. 173 - */ 174 - public async dereference({ 175 - fetch, 176 - pathOrUrlOrSchema, 177 - }: { 178 - fetch?: RequestInit; 179 - pathOrUrlOrSchema: JSONSchema | string | unknown; 180 - }): Promise<JSONSchema> { 181 - await this.parse({ 182 - fetch, 183 - pathOrUrlOrSchema, 184 - }); 185 - await resolveExternal(this, this.options); 186 - const errors = JSONParserErrorGroup.getParserErrors(this); 187 - if (errors.length > 0) { 188 - throw new JSONParserErrorGroup(this); 189 - } 190 - _dereference(this, this.options); 191 - const errors2 = JSONParserErrorGroup.getParserErrors(this); 192 - if (errors2.length > 0) { 193 - throw new JSONParserErrorGroup(this); 194 - } 195 - return this.schema!; 196 - } 197 - 198 - /** 199 165 * Parses the given JSON schema. 200 166 * This method does not resolve any JSON references. 201 167 * It just reads a single file in JSON or YAML format, and parse it as a JavaScript object. ··· 241 207 fetch, 242 208 file, 243 209 }); 244 - const parseResult = await parseFile(file, this.options); 210 + const parseResult = await parseFile(file, this.options.parse); 245 211 $refAdded.value = parseResult.result; 246 212 schema = parseResult.result; 247 - } catch (err) { 248 - if (isHandledError(err)) { 249 - $refAdded.value = err; 213 + } catch (error) { 214 + if (isHandledError(error)) { 215 + $refAdded.value = error; 250 216 } 251 - 252 - throw err; 217 + throw error; 253 218 } 254 219 } 255 220 ··· 306 271 fetch, 307 272 file, 308 273 }); 309 - const parseResult = await parseFile(file, this.options); 274 + const parseResult = await parseFile(file, this.options.parse); 310 275 $refAdded.value = parseResult.result; 311 276 schema = parseResult.result; 312 - } catch (err) { 313 - if (isHandledError(err)) { 314 - $refAdded.value = err; 277 + } catch (error) { 278 + if (isHandledError(error)) { 279 + $refAdded.value = error; 315 280 } 316 - 317 - throw err; 281 + throw error; 318 282 } 319 283 } 320 284
+5 -10
packages/json-schema-ref-parser/src/parse.ts
··· 31 31 /** 32 32 * Parses the given file's contents, using the configured parser plugins. 33 33 */ 34 - export const parseFile = async ( 34 + export async function parseFile( 35 35 file: FileInfo, 36 - options: $RefParserOptions, 37 - ): Promise<PluginResult> => { 36 + options: $RefParserOptions['parse'], 37 + ): Promise<PluginResult> { 38 38 try { 39 39 // If none of the parsers are a match for this file, try all of them. This 40 40 // handles situations where the file is a supported type, just with an 41 41 // unknown extension. 42 - const parsers = [ 43 - options.parse.json, 44 - options.parse.yaml, 45 - options.parse.text, 46 - options.parse.binary, 47 - ]; 42 + const parsers = [options.json, options.yaml, options.text, options.binary]; 48 43 const filtered = parsers.filter((plugin) => plugin.canHandle(file)); 49 44 return await plugins.run(filtered.length ? filtered : parsers, file); 50 45 } catch (error: any) { ··· 62 57 63 58 throw new ParserError(error.error.message, file.url); 64 59 } 65 - }; 60 + }
+5
packages/json-schema-ref-parser/src/refs.ts
··· 152 152 throw ono(`Error resolving $ref pointer "${path}". \n"${withoutHash}" not found.`); 153 153 } 154 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 + 155 160 return $ref.resolve(absPath, options, path, pathFromRoot); 156 161 } 157 162
+76 -45
packages/json-schema-ref-parser/src/resolve-external.ts
··· 21 21 * The promise resolves once all JSON references in the schema have been resolved, 22 22 * including nested references that are contained in externally-referenced files. 23 23 */ 24 - export function resolveExternal(parser: $RefParser, options: $RefParserOptions) { 25 - try { 26 - // console.log('Resolving $ref pointers in %s', parser.$refs._root$Ref.path); 27 - const promises = crawl(parser.schema, parser.$refs._root$Ref.path + '#', parser.$refs, options); 28 - return Promise.all(promises); 29 - } catch (e) { 30 - return Promise.reject(e); 31 - } 24 + export async function resolveExternal(parser: $RefParser, options: $RefParserOptions) { 25 + const promises = crawl(parser.schema, { 26 + $refs: parser.$refs, 27 + options: options.parse, 28 + path: `${parser.$refs._root$Ref.path}#`, 29 + }); 30 + await Promise.all(promises); 32 31 } 33 32 34 33 /** 35 34 * Recursively crawls the given value, and resolves any external JSON references. 36 35 * 37 36 * @param obj - The value to crawl. If it's not an object or array, it will be ignored. 38 - * @param path - The full path of `obj`, possibly with a JSON Pointer in the hash 39 - * @param {boolean} external - Whether `obj` was found in an external document. 40 - * @param $refs 41 - * @param options 42 - * @param seen - Internal. 43 - * 44 - * @returns 45 - * Returns an array of promises. There will be one promise for each JSON reference in `obj`. 37 + * @returns An array of promises. There will be one promise for each JSON reference in `obj`. 46 38 * If `obj` does not contain any JSON references, then the array will be empty. 47 39 * If any of the JSON references point to files that contain additional JSON references, 48 40 * then the corresponding promise will internally reference an array of promises. 49 41 */ 50 42 function crawl<S extends object = JSONSchema>( 51 43 obj: string | Buffer | S | undefined | null, 52 - path: string, 53 - $refs: $Refs<S>, 54 - options: $RefParserOptions, 55 - seen?: Set<any>, 56 - external?: boolean, 57 - ) { 58 - seen ||= new Set(); 59 - let promises: any = []; 44 + { 45 + $refs, 46 + external = false, 47 + options, 48 + path, 49 + seen = new Set(), 50 + }: { 51 + $refs: $Refs<S>; 52 + /** Whether `obj` was found in an external document. */ 53 + external?: boolean; 54 + options: $RefParserOptions['parse']; 55 + /** The full path of `obj`, possibly with a JSON Pointer in the hash. */ 56 + path: string; 57 + seen?: Set<unknown>; 58 + }, 59 + ): ReadonlyArray<Promise<unknown>> { 60 + let promises: Array<Promise<unknown>> = []; 60 61 61 62 if (obj && typeof obj === 'object' && !ArrayBuffer.isView(obj) && !seen.has(obj)) { 62 - seen.add(obj); // Track previously seen objects to avoid infinite recursion 63 + seen.add(obj); 64 + 63 65 if ($Ref.isExternal$Ref(obj)) { 64 - promises.push(resolve$Ref<S>(obj, path, $refs, options)); 66 + promises.push( 67 + resolve$Ref<S>(obj, { 68 + $refs, 69 + options, 70 + path, 71 + seen, 72 + }), 73 + ); 65 74 } 66 75 67 - const keys = Object.keys(obj) as string[]; 68 - for (const key of keys) { 69 - const keyPath = Pointer.join(path, key); 70 - const value = obj[key as keyof typeof obj] as string | JSONSchema | Buffer | undefined; 71 - promises = promises.concat(crawl(value, keyPath, $refs, options, seen, external)); 76 + for (const [key, value] of Object.entries(obj)) { 77 + promises = promises.concat( 78 + crawl(value, { 79 + $refs, 80 + external, 81 + options, 82 + path: Pointer.join(path, key), 83 + seen, 84 + }), 85 + ); 72 86 } 73 87 } 74 88 ··· 89 103 */ 90 104 async function resolve$Ref<S extends object = JSONSchema>( 91 105 $ref: S, 92 - path: string, 93 - $refs: $Refs<S>, 94 - options: $RefParserOptions, 95 - ) { 106 + { 107 + $refs, 108 + options, 109 + path, 110 + seen, 111 + }: { 112 + $refs: $Refs<S>; 113 + options: $RefParserOptions['parse']; 114 + path: string; 115 + seen: Set<unknown>; 116 + }, 117 + ): Promise<unknown> { 96 118 const resolvedPath = url.resolve(path, ($ref as JSONSchema).$ref!); 97 119 const withoutHash = url.stripHash(resolvedPath); 98 - 99 - // $ref.$ref = url.relative($refs._root$Ref.path, resolvedPath); 100 120 101 121 // If this ref points back to an input source we've already merged, avoid re-importing 102 122 // by checking if the path (without hash) matches a known source in parser and we can serve it internally later. ··· 105 125 const ref = $refs._$refs[withoutHash]; 106 126 if (ref) { 107 127 // We've already parsed this $ref, so crawl it to resolve its own externals 108 - const promises = crawl(ref.value as S, `${withoutHash}#`, $refs, options, new Set(), true); 128 + const promises = crawl(ref.value as S, { 129 + $refs, 130 + external: true, 131 + options, 132 + path: `${withoutHash}#`, 133 + seen, 134 + }); 109 135 return Promise.all(promises); 110 136 } 111 137 ··· 121 147 122 148 $refAdded.pathType = resolvedInput.type; 123 149 124 - let promises: any = []; 150 + let promises: ReadonlyArray<Promise<unknown>> = []; 125 151 126 152 if (resolvedInput.type !== 'json') { 127 153 const resolver = resolvedInput.type === 'file' ? fileResolver : urlResolver; 128 154 await resolver.handler({ file }); 129 155 const parseResult = await parseFile(file, options); 130 156 $refAdded.value = parseResult.result; 131 - promises = crawl(parseResult.result, `${withoutHash}#`, $refs, options, new Set(), true); 157 + promises = crawl(parseResult.result, { 158 + $refs, 159 + external: true, 160 + options, 161 + path: `${withoutHash}#`, 162 + seen, 163 + }); 132 164 } 133 165 134 166 return Promise.all(promises); 135 - } catch (err) { 136 - if (isHandledError(err)) { 137 - $refAdded.value = err; 167 + } catch (error) { 168 + if (isHandledError(error)) { 169 + $refAdded.value = error; 138 170 } 139 - 140 - throw err; 171 + throw error; 141 172 } 142 173 }
+2 -2
packages/json-schema-ref-parser/src/util/plugins.ts
··· 41 41 if (index === plugins.length) { 42 42 throw new Error('No promise has been returned.'); 43 43 } 44 - } catch (e) { 44 + } catch (error) { 45 45 lastError = { 46 - error: e, 46 + error, 47 47 plugin, 48 48 }; 49 49 runNextPlugin();
+1 -1
packages/openapi-python/src/createClient.ts
··· 89 89 : await refParser.bundle({ 90 90 arrayBuffer: specData[0]!.arrayBuffer, 91 91 pathOrUrlOrSchema: undefined, 92 - resolvedInput: specData[0]!.resolvedInput, 92 + resolvedInput: specData[0]!.resolvedInput!, 93 93 }); 94 94 95 95 // on subsequent runs in watch mode, print the message only if we know we're
+1 -1
packages/openapi-ts/src/createClient.ts
··· 89 89 : await refParser.bundle({ 90 90 arrayBuffer: specData[0]!.arrayBuffer, 91 91 pathOrUrlOrSchema: undefined, 92 - resolvedInput: specData[0]!.resolvedInput, 92 + resolvedInput: specData[0]!.resolvedInput!, 93 93 }); 94 94 95 95 // on subsequent runs in watch mode, print the message only if we know we're