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

Fix regression by using global visited set for $ref following

Add global visited set to prevent schemas from being walked multiple times
when following $refs. This fixes the regression where BroadcastedResponses
was incorrectly using LocationResponseWritable instead of LocationResponse.

The fix ensures that each schema is only walked once (from the first context
it's encountered in), which prevents conflicting updates when the same schema
is referenced from multiple paths.

Co-authored-by: mrlubos <12529395+mrlubos@users.noreply.github.com>

+12 -6
+1 -1
packages/openapi-ts-tests/main/test/__snapshots__/3.0.x/transforms-properties-required-by-default/types.gen.ts
··· 194 194 }; 195 195 196 196 export type BroadcastedResponses = { 197 - responses: Array<LocationResponseWritable> | null; 197 + responses: Array<LocationResponse> | null; 198 198 }; 199 199 200 200 export type BulkCreateOrUpdateHarvest = {
+11 -5
packages/openapi-ts/src/openApi/shared/transforms/readWrite.ts
··· 483 483 }): void => { 484 484 const event = logger.timeEvent('update-refs-in-spec'); 485 485 const schemasPointerNamespace = specToSchemasPointerNamespace(spec); 486 + const globalVisited = new Set<string>(); // Track globally visited schemas to prevent multiple walks 486 487 487 488 const walk = ({ 488 489 context, ··· 634 635 } else if (key === '$ref' && typeof value === 'string') { 635 636 // Prefer exact match first 636 637 const map = split.mapping[value]; 637 - if (map) { 638 - if (map.read && (!nextContext || nextContext === 'read')) { 638 + if (map && nextContext) { 639 + // Only update $refs when we have a clear read/write context 640 + // This avoids defaulting to read variant when context is null 641 + if (map.read && nextContext === 'read') { 639 642 (node as Record<string, unknown>)[key] = map.read; 640 - } else if (map.write && (!nextContext || nextContext === 'write')) { 643 + } else if (map.write && nextContext === 'write') { 641 644 (node as Record<string, unknown>)[key] = map.write; 642 645 } 643 646 } else if ( 644 647 inSchema && 645 648 nextContext && 646 649 value.startsWith(schemasPointerNamespace) && 647 - !visited.has(value) 650 + !visited.has(value) && 651 + !globalVisited.has(value) // Prevent walking the same schema multiple times 648 652 ) { 649 653 // If we're in a schema with a defined context (read/write), follow the $ref 650 654 // to update nested $refs within the referenced schema 651 - // Use visited set to avoid infinite recursion on circular references 655 + // Use visited set to avoid circular references, and global visited to prevent 656 + // multiple walks of the same schema from different paths 652 657 const schemasObj = getSchemasObject(spec); 653 658 if (schemasObj) { 654 659 const schemaName = value.substring( ··· 658 663 if (referencedSchema) { 659 664 const newVisited = new Set(visited); 660 665 newVisited.add(value); 666 + globalVisited.add(value); // Mark as globally visited 661 667 walk({ 662 668 context: nextContext, 663 669 currentPointer: value,